SQLAlchemy FK ondelete不会限制

时间:2019-05-03 11:30:48

标签: python sqlalchemy self-referencing-table

我建立了自我参照关系。一个人可以有一个单亲父母(或无),一个人可以有多个孩子(或无)。

因此允许 NULL 作为FK:

class Person(db.Model):
    id        = db.Column(db.Integer, primary_key=True)
    parent_id = db.Column(db.Integer, db.ForeignKey('person.id', ondelete='RESTRICT'))
    parent    = db.relationship('Person', remote_side=[id], back_populates='children')
    children  = db.relationship('Person', back_populates='parent')

但是,如果他们是父母,我希望禁止删除。因此,我包含了ondelete='RESTRICT'子句,但是它没有任何作用。删除父级后,parent_id列仍设置为NULL。

(请注意,我的SQLite连接已将编译指示外键约束转换为ON)

为什么在删除父级时数据库不引发错误,因此以其为外键的子列限制了此错误?

2 个答案:

答案 0 :(得分:2)

Sqlalchemy在数据库有机会评估外键约束之前使子行为空。如果将passive_deletes=True添加到关系中,则sqlalchemy 不会尝试管理相关实体的删除,而只是让数据库根据您的配置方式来做。将不会。在删除父对象之前,请先发出选择以填充关系。设置为True仍将导致会话中已有子对象的FK列设置为NULL

此配置:

class Person(db.Model):
    id        = db.Column(db.Integer, primary_key=True)
    parent_id = db.Column(db.Integer, db.ForeignKey('person.id', ondelete='RESTRICT'))
    parent    = db.relationship('Person', remote_side=[id], back_populates='children')
    children  = db.relationship('Person', back_populates='parent', passive_deletes=True)


if __name__ == '__main__':
    with app.app_context():
        db.drop_all()
        db.create_all()
        parent = Person()
        db.session.add(parent)
        child = Person(parent=parent)
        db.session.commit()
        db.session.delete(parent)
        db.session.commit()

提高:

  

sqlalchemy.exc.IntegrityError:(mysql.connector.errors.IntegrityError)   1451(23000):无法删除或更新父行:外键   约束失败(testperson,约束person_ibfk_1 FOREIGN   KEY(parent_id参考(personid))

if __name__ == '__main__':
    with app.app_context():
        db.drop_all()
        db.create_all()
        parent = Person()
        db.session.add(parent)
        child = Person(parent=parent)
        db.session.commit()
        db.session.query(Person).all()  # reload the people into the session before deleting parent
        db.session.delete(parent)
        db.session.commit()

...即使使用parent_id,仍将使孩子的passive_deletes=True字段为空。因此passive_deletes='all'是必经之路。

答案 1 :(得分:2)

您的外键约束设置看起来正确,但是您的ORM关系没有显式的级联配置,因此它们使用的是默认值save-updatemerge。在此默认配置中,通过将孤儿的外键设置为childrenNULL关系可以在删除父级时取消其关联。我认为您在这种情况下应该使用passive_deletes='all'(请参阅delete级联的注释)来在删除父级时禁用任何ORM级别的级联,以便数据库在刷新时可以防止删除: / p>

class Person(db.Model):
    id        = db.Column(db.Integer, primary_key=True)
    parent_id = db.Column(db.Integer, db.ForeignKey('person.id', ondelete='RESTRICT'))
    parent    = db.relationship('Person', remote_side=[id], back_populates='children')
    children  = db.relationship('Person', back_populates='parent', passive_deletes='all')