【问题标题】:Cascade delete-orphan with Generic Associations (multiple parents) in sqlalchemysqlalchemy 中具有通用关联(多个父项)的级联删除孤儿
【发布时间】:2017-04-26 08:54:05
【问题描述】:

我有一个带有两个外键的 sqlalchemy 实体,因为它可以有两个完全不同的父级之一。让我们使用 AddressCustomerSupplier 来进行说明。 Address 可以属于 CustomerSupplier(在我的情况下可以有孩子)。

我希望地址及其子代在其父代为零时自动删除,即 CustomerSupplier 均未提及它。

我最初是这样实现的

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-relatedtable-per-association。不过,这两个似乎都没有解决删除级联的问题:

  • table-per-related:因为 Address 有它自己的孩子,我必须复制每个相关的地址下面的整个层次结构。否则问题只会转移到 Address 的孩子身上
  • table-per-association:在 Address 上安装了反向引用,这似乎与我的初始实现相同,只是现在直接父级是关联表中的行。我看不出delete-orphan 在这里如何工作。

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

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

    找到了答案。 SO上有几个relatedquestions,人们在多对多关系中遇到了这个问题(基本上是每个关联的表)。还有一个 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() 添加到查询语句中。这是因为后者会绕过 Address 实体上的级联,因此不会删除 Address 的子代。

    【讨论】:

      猜你喜欢
      • 2013-06-05
      • 2012-08-16
      • 2016-10-24
      • 2010-10-18
      • 1970-01-01
      • 2014-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多