首页 > 解决方案 > SQLAlchemy FK ondelete 不限制

问题描述

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

因此允许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')

但是,如果 Person 是父母,我想禁止删除他们。所以我包含了该ondelete='RESTRICT'子句,但它没有效果。parent_id 列在删除父级时仍设置为 NULL。

(注意我的 SQLite 连接已将 pragma 外键约束设置为 ON)

为什么删除父级时数据库不会抛出错误,因此以它作为外键的子列会限制这一点?

标签: pythonsqlalchemyself-referencing-table

解决方案


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):无法删除或更新父行:外键约束失败(test. person,CONSTRAINT person_ibfk_1FOREIGN KEY(parent_id)REFERENCES 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'走的路也是如此。


推荐阅读