SQLAlchemy FK ondelete 不限制
SQLAlchemy FK ondelete does not RESTRICT
我建立了自我参照关系。一个人可以有一个parent(或None),一个人可以有多个child仁(或None)。
所以允许 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')
但是,我希望禁止 删除 parent 的人物。所以我包含了 ondelete='RESTRICT'
子句,但它没有任何效果。删除 parent 后,parent_id 列仍设置为 NULL。
(注意我的 SQLite 连接已将 pragma 外键约束切换为 ON)
为什么当 parent 被删除时数据库不抛出错误,因此 child 列以它作为外键限制了这个?
在数据库有机会评估外键约束之前,Sqlalchemy 正在清空子行。如果您将 passive_deletes=True
添加到关系中,sqlalchemy 将不会尝试管理相关实体的删除,而只是让数据库根据您的配置方式执行操作。 不会先发出 select 来填充关系,然后再删除父级。设置为 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): Cannot delete or update a parent row: a foreign key
constraint fails (test
.person
, CONSTRAINT person_ibfk_1
FOREIGN
KEY (parent_id
) REFERENCES person
(id
))
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'
是正确的选择。
您的外键约束设置看起来是正确的,但是您的 ORM 关系没有明确的级联配置,因此它们使用默认值 save-update and merge. In this default configuration the children
relationship de-associates orphaned children when deleting the parent by setting their foreign key to NULL
. I think you should use passive_deletes='all'
(see the note on delete 级联)在这种情况下删除父级时禁用任何 ORM 级级联,以便数据库在刷新时可以防止删除:
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')
我建立了自我参照关系。一个人可以有一个parent(或None),一个人可以有多个child仁(或None)。
所以允许 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')
但是,我希望禁止 删除 parent 的人物。所以我包含了 ondelete='RESTRICT'
子句,但它没有任何效果。删除 parent 后,parent_id 列仍设置为 NULL。
(注意我的 SQLite 连接已将 pragma 外键约束切换为 ON)
为什么当 parent 被删除时数据库不抛出错误,因此 child 列以它作为外键限制了这个?
在数据库有机会评估外键约束之前,Sqlalchemy 正在清空子行。如果您将 passive_deletes=True
添加到关系中,sqlalchemy 将不会尝试管理相关实体的删除,而只是让数据库根据您的配置方式执行操作。 不会先发出 select 来填充关系,然后再删除父级。设置为 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): Cannot delete or update a parent row: a foreign key constraint fails (
test
.person
, CONSTRAINTperson_ibfk_1
FOREIGN KEY (parent_id
) REFERENCESperson
(id
))
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'
是正确的选择。
您的外键约束设置看起来是正确的,但是您的 ORM 关系没有明确的级联配置,因此它们使用默认值 save-update and merge. In this default configuration the children
relationship de-associates orphaned children when deleting the parent by setting their foreign key to NULL
. I think you should use passive_deletes='all'
(see the note on delete 级联)在这种情况下删除父级时禁用任何 ORM 级级联,以便数据库在刷新时可以防止删除:
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')