如何定义SQLAlchemy FK和关系以允许数据库级联删除?

时间:2018-11-27 21:22:22

标签: python sqlalchemy

如何定义一个ForeignKey和关系,以便可以禁用SQLAlchemy的FK无效行为? here文档似乎描述了 passive_deletes=True允许数据库级联删除,但仅在定义cascade关系的上下文中 属性documented here,似乎是一个属性 对我来说,定义了SQLAlchemy如何自行执行级联删除,被明确描述为比数据库引擎的慢 级联删除 in this section (请参见标题为 ORM级“删除”级联与“外键”级“ ON DELETE”级联的绿色框。)

要使用数据库的级联删除,我们应该执行以下操作吗?

  1. ondelete="CASCADE"列上定义ForeignKey
  2. 在相同关系上定义passive_deletes=True
  3. AND 在对象之间的所有关系上定义一个cascade="delete, delete-orphan"参数?

这是我似乎很困惑的第3步:它似乎是在为SQLAlchemy定义级联,而不是允许数据库 执行它自己的删除。但是SQLAlchemy似乎想在数据库可以获取 之前清空所有依赖的外键。 有机会进行级联删除。我需要禁用此行为,但是passive_deletes=True似乎不是自己完成的。

(最新)答案here明确解决了我的问题,但是没有用。他说

  

这里有一个重要的警告。注意我如何与passive_deletes=True指定关系?如果您没有该功能,则整个操作将无法进行。   这是因为默认情况下,当您删除父记录时,SqlAlchemy所做的事情确实很奇怪。   它将所有子行的外键设置为NULL。因此,如果您从parent_table的{​​{1}}处删除一行,那么它将基本上执行   id = 5

在我的代码中

UPDATE child_table SET parent_id = NULL WHERE parent_id = 5

仅在父级关系上设置class Annotation(SearchableMixin, db.Model): id = db.Column(db.Integer, primary_key=True) locked = db.Column(db.Boolean, index=True, default=False) active = db.Column(db.Boolean, default=True) HEAD = db.relationship("Edit", primaryjoin="and_(Edit.current==True," "Edit.annotation_id==Annotation.id)", uselist=False, lazy="joined", passive_deletes=True) edits = db.relationship("Edit", primaryjoin="and_(Edit.annotation_id==Annotation.id," "Edit.approved==True)", lazy="joined", passive_deletes=True) history = db.relationship("Edit", primaryjoin="and_(Edit.annotation_id==Annotation.id," "Edit.approved==True)", lazy="dynamic", passive_deletes=True) all_edits = db.relationship("Edit", primaryjoin="Edit.annotation_id==Annotation.id", lazy="dynamic", passive_deletes=True) class Edit(db.Model): id = db.Column(db.Integer, primary_key=True) edit_num = db.Column(db.Integer, default=0) approved = db.Column(db.Boolean, default=False, index=True) rejected = db.Column(db.Boolean, default=False, index=True) annotation_id = db.Column(db.Integer, db.ForeignKey("annotation.id", ondelete="CASCADE"), index=True) hash_id = db.Column(db.String(40), index=True) current = db.Column(db.Boolean, default=False, index=True, passive_deletes=True) annotation = db.relationship("Annotation", foreign_keys=[annotation_id]) previous = db.relationship("Edit", primaryjoin="and_(remote(Edit.annotation_id)==foreign(Edit.annotation_id)," "remote(Edit.edit_num)==foreign(Edit.edit_num-1))") priors = db.relationship("Edit", primaryjoin="and_(remote(Edit.annotation_id)==foreign(Edit.annotation_id)," "remote(Edit.edit_num)<=foreign(Edit.edit_num-1))", uselist=True, passive_deletes=True) 无效。我也认为这可能是由于这种关系造成的 从孩子到兄弟姐妹(关系passive_deletes=TrueEdit.previous),但在这两个关系上设置Edit.priors 不能解决问题,当我只运行passive_deletes=True时,它会导致以下警告:

  

/home/malan/projects/icc/icc/venv/lib/python3.7/site-packages/sqlalchemy/orm/relationships.py:1790:SA警告:在Edit.previous上,“ passive_deletes”通常配置为一对多,一对一,多对多关系。     %self)

     

/home/malan/projects/icc/icc/venv/lib/python3.7/site-packages/sqlalchemy/orm/relationships.py:1790:SAWarning:在Edit.priors上,“ passive_deletes”通常配置为一对多,一对一,多对多关系。     %self)

我实际上发现了2015年的this有趣的问题,但从未得到答案。详细介绍了执行文档代码失败的尝试。

1 个答案:

答案 0 :(得分:0)

似乎在尝试分析我的人际关系后,我发现了问题所在。

首先,我要注意,passive_deletes=True是仅 必需的参数。您根本不需要定义cascade来利用数据库的级联系统。

更重要的是,我的问题似乎源于我的外键依赖树。我有一个看起来像这样的小瀑布:

       Annotation
      /     |    \ 
   Vote    Edit   annotation_followers
           /  \ 
    EditVote   tags

在每个子类的每个ondelete="CASCADE"列中定义了parent_id的位置。直到我在图中的 all 个子级上设置了active_deletes为止,无效行为仍然不正常。

对于遇到类似问题的任何人,我的建议是:彻底分析您所有相交的关系,并在所有有意义的条件下定义passive_deletes=True

那是说,我还在解决一些并发症;例如,在多对多表中,id甚至没有失效。可能的下一个问题。