【问题标题】:How to prevent this circular reference in Flask/SQLAlchemy Models?如何在 Flask/SQLAlchemy 模型中防止这种循环引用?
【发布时间】:2016-11-07 08:06:39
【问题描述】:

我的 Flask/SQLAlchemy 应用程序中有以下四个模型类(第五个类 MyClassA 未在此处显示,但作为参考):

class MyClassF(db.Model):
    valid_fs = ['F1', 'F2']
    f_id = db.Column(db.Integer, nullable=False, primary_key=True)
    name = db.Column(db.Enum(*valid_fs, name='f_enum'), default='F1', nullable=False)

class MyClassD(db.Model):
    d_id = db.Column(db.Integer, nullable=False, primary_key=True)
    name = db.Column(db.String(128))
    v_collection = db.relationship('MyClassV', lazy='dynamic', backref=db.backref('d'), cascade='all, delete-orphan')

class MyClassV(db.Model):
    v_id = db.Column(db.Integer, nullable=False, primary_key=True)
    d_id = db.Column(db.Integer, db.ForeignKey(MyClassD.d_id), nullable=False)
    c_collection = db.relationship('MyClassC', lazy='dynamic', backref=db.backref('v'), cascade='all, delete-orphan')
    a_collection = db.relationship('MyClassA', lazy='dynamic', backref=db.backref('v'), cascade='all, delete-orphan')

class MyClassC(db.Model):
    c_id = db.Column(db.Integer, nullable=False, primary_key=True)
    v_id = db.Column(db.Integer, db.ForeignKey(MyClassV.v_id), nullable=False)
    f_id = db.Column(
        db.Integer,
        db.ForeignKey(MyClassF.f_id),
        nullable=False,
        #default=MyClassF.query.filter(MyClassF.name == "F1").one().f_id
    )

使用Flask-Migrate 命令db initdb migratedb upgrade 创建这个架构就可以了。

但是,当我取消注释 MyClassC.f_id 的定义并再次尝试时(删除 migrations 目录后),我收到以下循环依赖错误:

sqlalchemy.exc.InvalidRequestError:初始化映射器时 Mapper|MyClassV|my_classV,表达式“MyClassC”未能找到 名称(“未定义名称'MyClassC'”)。如果这是一个类名, 考虑将此关系()添加到 在定义了两个依赖类之后的类。

我要做的就是确保通过查询MyClassF 表来设置MyClassC.f_id 的默认值。此检查应在插入时进行——而不是在创建数据库时。所以我不明白为什么我现在收到这个错误。

我如何使用db.relationship()(或任何其他技术)来解决这个循环依赖错误,同时执行我正在尝试实施的数据库完整性规则?

【问题讨论】:

    标签: python sqlalchemy flask-sqlalchemy circular-dependency flask-migrate


    【解决方案1】:

    我相信默认参数被翻译成SQL DEFAULT constraint。 AFAICT 此约束不会在插入时进行评估,但在创建表时将该值用作默认值(这意味着默认值在创建表时设置一次并用于所有缺少该列的行,它不能根据另一个表的内容动态改变)。

    然而the documentation of SQLAlchemy 提到你可以传入一个python 函数 作为默认值,每次插入都会调用它来获取要使用的默认值,所以你可以这样做:

    class MyClassC(db.Model):
        c_id = db.Column(db.Integer, nullable=False, primary_key=True)
        v_id = db.Column(db.Integer, db.ForeignKey(MyClassV.v_id), nullable=False)
        f_id = db.Column(
            db.Integer,
            db.ForeignKey(MyClassF.f_id),
            nullable=False,
            default=lambda: MyClassF.query.filter(MyClassF.name == "F1").one().f_id
        )
    

    但是请注意,这不会使用任何数据库工具来提供默认值,它是 SQLAlchemy 首先获取默认值,然后手动将其插入到查询中。

    【讨论】:

    • 完美答案。谢谢!
    猜你喜欢
    • 2015-02-19
    • 1970-01-01
    • 1970-01-01
    • 2020-11-11
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    • 2018-07-20
    • 1970-01-01
    相关资源
    最近更新 更多