如何在Flask / SQLAlchemy模型中阻止此循环引用?

时间:2016-11-07 08:06:40

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

我在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时   Mapper | MyClassV | my_classV,表达式'MyClassC'找不到   name(“name'MyClassC'未定义”)。如果这是一个类名,   考虑将此关系()添加到   在定义了两个依赖类之后的类。

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

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

1 个答案:

答案 0 :(得分:3)

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

但是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首先获取默认值,然后手动将其插入查询中。