Flask SQLAlchemy多对多插入数据


77

我试图在Flask-SQLAlchemy中建立多对多关系,但似乎我不知道如何填写“多对多标识符数据库”。您能帮我了解我在做什么错以及应该怎么看吗?

class User(db.Model):
    __tablename__ = 'users'
    user_id = db.Column(db.Integer, primary_key=True)
    user_fistName = db.Column(db.String(64))
    user_lastName = db.Column(db.String(64))
    user_email = db.Column(db.String(128), unique=True)


class Class(db.Model):
    __tablename__ = 'classes'
    class_id = db.Column(db.Integer, primary_key=True)
    class_name = db.Column(db.String(128), unique=True)

然后是我的标识符数据库:

student_identifier = db.Table('student_identifier',
    db.Column('class_id', db.Integer, db.ForeignKey('classes.class_id')),
    db.Column('user_id', db.Integer, db.ForeignKey('users.user_id'))
)

到目前为止,当我尝试将数据插入数据库时​​,看起来像这样。

# User
user1 = User(
            user_fistName='John',
            user_lastName='Doe',
            user_email='john@doe.es')

user2 = User(
            user_fistName='Jack',
            user_lastName='Doe',
            user_email='jack@doe.es')

user3 = User(
            user_fistName='Jane',
            user_lastName='Doe',
            user_email='jane@doe.es')

db.session.add_all([user1, user2, user3])
db.session.commit()

# Class
cl1 = Class(class_name='0A')
cl2 = Class(class_name='0B')
cl3 = Class(class_name='0C')
cl4 = Class(class_name='Math')
cl5 = Class(class_name='Spanish')
db.session.add_all([cl1, cl2, cl3, cl4, cl5])
db.session.commit()

现在我的问题是,由于我真的无法创建“ student_identifier”对象,如何将其添加到多对多数据库?如果可以的话,它可能看起来像这样:

# Student Identifier
sti1  = StiClass(class_id=cl1.class_id, class_name=user1.user_id)
sti2  = StiClass(class_id=cl3.class_id, class_name=user1.user_id)
sti3  = StiClass(class_id=cl4.class_id, class_name=user1.user_id)
sti4  = StiClass(class_id=cl2.class_id, class_name=user2.user_id)
db.session.add_all([sti1, sti2, sti3, sti4])
db.session.commit()

我应该如何用ORM插入多对多表?

Answers:


143

您不需要直接在关联表中添加任何内容,SQLAlchemy会这样做。这或多或少来自SQLAlchemy文档

association_table = db.Table('association', db.Model.metadata,
    db.Column('left_id', db.Integer, db.ForeignKey('left.id')),
    db.Column('right_id', db.Integer, db.ForeignKey('right.id'))
)

class Parent(db.Model):
    __tablename__ = 'left'
    id = db.Column(db.Integer, primary_key=True)
    children = db.relationship("Child",
                    secondary=association_table)

class Child(db.Model):
    __tablename__ = 'right'
    id = db.Column(db.Integer, primary_key=True)


p = Parent()
c = Child()
p.children.append(c)
db.session.add(p)
db.session.commit()

因此,您的示例将如下所示:

student_identifier = db.Table('student_identifier',
    db.Column('class_id', db.Integer, db.ForeignKey('classes.class_id')),
    db.Column('user_id', db.Integer, db.ForeignKey('students.user_id'))
)

class Student(db.Model):
    __tablename__ = 'students'
    user_id = db.Column(db.Integer, primary_key=True)
    user_fistName = db.Column(db.String(64))
    user_lastName = db.Column(db.String(64))
    user_email = db.Column(db.String(128), unique=True)


class Class(db.Model):
    __tablename__ = 'classes'
    class_id = db.Column(db.Integer, primary_key=True)
    class_name = db.Column(db.String(128), unique=True)
    students = db.relationship("Student",
                               secondary=student_identifier)

s = Student()
c = Class()
c.students.append(s)
db.session.add(c)
db.session.commit()

12
假设我的学生已经存在于表格中。如何将它们附加到班级上,而不必每次都查询每个学生的全部数据(仅使用前导键)?
axwell

@axwell,如果您经常要求学生上课(例如,在检查更新时),则可以像这样用lazy ='subquery'调整关系: students = db.relationship("Student", secondary=student_identifier, lazy='subquery', backref=db.backref('classes', lazy=True))
Andrii Danyleiko

20

首先,student_identifier被定义为SQLAlchemy反射表而不是数据库。

通常,如果在模型和反射表对象之间正确设置了所有关系,则只需处理相关模型(通过将模型对象追加到关系InstrumentList中)即可将数据插入反射表,例如,答案@上面提供的mehdi-sadeghi。

但是,如果您不想建立关系,确实可以直接插入反射表中。例如:

statement = student_identifier.insert().values(class_id=cl1.id, user_id=sti1.id)
db.session.execute(statement)
db.session.commit()

之后,您应该能够看到在student_identifier反射表中插入了多对多关系行。执行每个SQL语句后,不要忘了提交,就像在事务中一样。

希望对您有所帮助。


如何导入此反射表或关联表?
吉宾·马修

@jibinmathew就像使用类和其他符号一样。
Devy

@jibinmathew导入关联表时,我也遇到了问题,因此我在本地调用该表,正是在上面的地方。
Tri

1
@GofyandKitty可能是代码库中循环依赖项/导入的指示器,因此您需要使用本地导入(以提供名称空间)才能使其工作。
Devy

1
非常感谢您分享此解决方案。您从字面上救了我
Chidiebere

-1

要扩展问题,您还可以使用一次添加多个条目extend

class_ = db.session.query(Class).first()
new_students = db.session.query(Student).all()
class_.students.extend(new_students)
db.session.add(class_)
db.session.commit()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.