I am trying to write a generic auditing extension, but I have some inconsistencies I'd like to iron out if possible.
The problem I have is that when an object is updated via a relationship rather than a Column attribute, the value returned from attributes.get_history is a SQLAlchemy object, and not the value that is in the database. This is more pronounced on a many-to-many relationship - the secondary table isn't marked as new/dirty, even though that is the table that will actually have data inserted into it. Instead the 2 tables joined by the secondary table are marked as dirty, although no data is changed in those tables. I understand this makes sense at the SQLAlchemy level - but is there a way to get the actual database changes? A code example to illustrate this: from sqlalchemy.types import Integer from sqlalchemy import Column, ForeignKey, create_engine from sqlalchemy.orm import relationship, backref, attributes, sessionmaker from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm.interfaces import SessionExtension BASE = declarative_base() ENGINE = create_engine("sqlite://") class AuditListener(SessionExtension): def after_flush(self, session, *args): for attr in ("new", "dirty", "deleted"): print attr for obj in getattr(session, attr): print " %s" % obj.__class__.__name__ for col in obj.__mapper__.iterate_properties: added, unchanged, deleted = attributes.get_history(obj, col.key) if added: print " %s: %s" % (col.key, str(added)) SESSION = sessionmaker( bind=ENGINE, extension=(AuditListener()) ) class FooBar(BASE): __tablename__ = 'foobar' foo_id = Column('foo_id', ForeignKey('foo.foo_id'), primary_key=True) bar_id = Column('bar_id', ForeignKey('bar.bar_id'), primary_key=True) class Foo(BASE): __tablename__ = 'foo' foo_id = Column('foo_id', Integer, primary_key=True) bars = relationship('Bar', secondary=FooBar.__table__, backref=backref('foos')) class Bar(BASE): __tablename__ = 'bar' bar_id = Column('bar_id', Integer, primary_key=True) def example(): BASE.metadata.create_all(ENGINE, checkfirst=False) session = SESSION() f = Foo(foo_id=1) b1 = Bar(bar_id=1) b2 = Bar(bar_id=2) session.add(f) session.add(b1) session.add(b2) session.commit() # add relationship between f and b1 & b2: f.bars = [b1, b2] session.commit() if __name__ == '__main__': example() If you run this code you will see this output: new Bar bar_id: [2] Foo foo_id: [1] Bar bar_id: [1] dirty deleted new dirty Bar Foo bars: [<__main__.Bar object at 0x27b4910>, <__main__.Bar object at 0x277b050>] Bar deleted As you can see, the output when the many-to-many relationship is committed is to denote an update to the bars relationship of Foo. There is nothing indicating there are 2 new FooBar entries created. Any ideas? Thanks, Mark. -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/groups/opt_out.