How can I detect when instances are deleted via a "delete-orphan" cascade?

91 Views Asked by At

I'm trying to add audit logging for some of my tables, which I've implemented in an after_flush listener. By accessing the session state in session.new/dirty/deleted, I can get the information I need.

Well, at least for most cases: I'm having no luck identifying instances that get deleted via a "delete-orphan" cascade. Those instances don't show up in session.deleted, but instead in session.dirty and I can't find a way to determine whether they will be deleted.

Using this toy model to illustrate:

class Author(Base):
    __tablename__ = 'authors'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    posts = relationship('Post', back_populates='author', cascade='all, delete-orphan')

    def __repr__(self):
        return 'Author(name={0.name})'.format(self)

class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    content = Column(String)
    author_id = Column(Integer, ForeignKey('authors.id'))
    author = relationship('Author', back_populates='posts')

    def __repr__(self):
        return 'Post(content={0.content})'.format(self)

Identifying regular adds/updates/deletes works as intended:

In [1]: session = Session()
   ...: jane = Author(name='Jane')
   ...: jane.posts = [Post(content='The nature of things'), Post(content='On the origin of stuff')]
   ...: session.add(jane)
   ...: session.new
Out[1]: IdentitySet([Author(name=Jane), Post(content=On the origin of stuff), Post(content=The nature of things)])

In [2]: session.flush()
   ...: jane.name = 'John'
   ...: session.dirty
Out[2]: IdentitySet([Author(name=John)])

In [3]: session.flush()
   ...: post = jane.posts[0]
   ...: session.delete(post)
   ...: session.deleted
Out[3]: IdentitySet([Post(content=The nature of things)])

All good so far. However, when I update the Author's Posts via the relationship, which causes the "origin of stuff" Post to be deleted via the cascade, this eliminated post only show up as dirty, not as deleted:

In [4]: session.flush()
   ...: jane.posts = [Post(content='Matter matters!')]
   ...: session.deleted
Out[4]: IdentitySet([])

In [5]: session.dirty
Out[5]: IdentitySet([Author(name=Jane), Post(content=On the origin of stuff)])

How can I detect that this Post will be (or has been, in the case of an after_flush listener) deleted?

0

There are 0 best solutions below