Flask sqlalchemy 多对多插入数据

Posted

技术标签:

【中文标题】Flask sqlalchemy 多对多插入数据【英文标题】:Flask sqlalchemy many-to-many insert data 【发布时间】:2014-10-29 08:34:37 【问题描述】:

我试图在 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 插入多对多表?

【问题讨论】:

【参考方案1】:

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

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()

【讨论】:

假设我的学生已经存在于一个表中。如何将它们附加到一个班级,而不必每次都查询每个学生的全部数据(仅使用外键)? @axwell,如果您经常需要学生上课(即检查更新时),您可以像这样调整与 lazy='subquery' 的关系:students = db.relationship("Student", secondary=student_identifier, lazy='subquery', backref=db.backref('classes', lazy=True)) @axwell 如果学生已经存在,您也可以这样插入:session.execute( student_identifier.insert(), params="class_id": class_id, "user_id": user_id, )【参考方案2】:

首先,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 就像使用类和其他符号一样。 @jibinmathew 我在导入关联表时也遇到了问题,所以我在本地导入表,就在上面我调用它的地方。 @GofyandKitty 这可能是代码库中循环依赖/导入的指标,因此您需要使用本地导入(提供命名空间)才能使其工作。 非常感谢分享这个解决方案。你真的救了我【参考方案3】:

要扩展牛鳃答案,您还可以使用 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()

【讨论】:

以上是关于Flask sqlalchemy 多对多插入数据的主要内容,如果未能解决你的问题,请参考以下文章

Flask SQLAlchemy 多对多插入重复条目

Flask SQLAlchemy 数据库多对多

Flask-SQLAlchemy 配置,处理对象-关系,一对多,多对多

深入浅出Flask(50):flask_sqlalchemy的创建多对多的关系表

Flask Marshmallow/SqlAlchemy:序列化多对多关系

Flask SQLAlchemy 多对多关联对象错误