在sqlalchemy中使用通用关联(多个父级)级联删除孤立

时间:2017-04-26 08:54:06

标签: python sqlalchemy

我有一个sqlalchemy实体有两个外键,因为它可以有两个完全不同的父母之一。我们使用地址客户供应商进行说明。 地址可以属于客户供应商(在我的情况下也可以有子女)。

我希望地址及其子项在父项为零时自动删除,即 Customer Supplier 都不会引用它。

我最初将其实现为

class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    customer_id = Column(Integer, ForeignKey('customers.id')
    supplier_id = Column(Integer, ForeignKey('suppliers.id')
    # etc ...

根据sqlalchemy文档,实现Generic Associations的规范方法是使用 table-per-related table-per-association 。这两个似乎都没有解决删除级联的问题:

  • table-per-related:由于地址有自己的子项,我将不得不复制每个相关地址下面的整个层次结构。否则问题只会转移到地址
  • 的子项
  • table-per-association:在地址上安装了后向引用,这似乎与我的初始实现相同,但现在直接父项是关联表中的行。我不知道delete-orphan如何在这里工作。

在sqlalchemy中使用通用关联处理delete-orphan级联的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

找到答案。在SO上有几个related questions,人们在多对多关系中遇到这个问题(基本上是每个关联表是什么)。还有一个SQLAlchemy Wiki条目,描述了如何解决问题。

解决方案是基本上通过实现在每次刷新后清理的事件监听器来手动执行delete-orphan

@event.listens_for(Session, 'after_flush')
def delete_address_orphans(session, ctx):
    if any(isinstance(i, Address) for i in session.dirty):
        query = session.query(Address).\
                filter_by(customer=None, supplier=None)
        orphans = query.all()
        for orphan in orphans:
            session.delete(orphan)

请注意,在我的情况下,我首先加载孤立,而不是简单地将.delete()添加到查询语句中。这是因为后者会绕过地址实体上的级联,因此不会删除地址的子节点。