我建立了自我参照关系。一个人可以有一个单亲父母(或无),一个人可以有多个孩子(或无)。
因此允许 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)
为什么在删除父级时数据库不引发错误,因此以其为外键的子列限制了此错误?
答案 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):无法删除或更新父行:外键 约束失败(
test
。person
,约束person_ibfk_1
FOREIGN KEY(parent_id
参考(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'
是必经之路。
答案 1 :(得分:2)
您的外键约束设置看起来正确,但是您的ORM关系没有显式的级联配置,因此它们使用的是默认值save-update和merge。在此默认配置中,通过将孤儿的外键设置为children
,NULL
关系可以在删除父级时取消其关联。我认为您在这种情况下应该使用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')