How can I get a SQLAlchemy ORM object's previous state after a db update?

crud, orm, python, sqlalchemy

Solution

My solution was to use 'after_flush' SessionEvent instead of 'set' AttributeEvent.

Many thanks to agronholm who provided example SessionEvent code that specifically checked an object's value and oldvalue.

The solution below is a modification of his code:

def get_old_value(attribute_state):
    history = attribute_state.history
    return history.deleted[0] if history.deleted else None


def trigger_attribute_change_events(object_):
    for mapper_property in object_mapper(object_).iterate_properties:
        if isinstance(mapper_property, ColumnProperty):
            key = mapper_property.key
            attribute_state = inspect(object_).attrs.get(key)
            history = attribute_state.history

            if history.has_changes():
                value = attribute_state.value
                # old_value is None for new objects and old value for dirty objects
                old_value = get_old_value(attribute_state)
                handler = registry.get(mapper_property)
                if handler:
                    handler(object_, value, old_value)


def on_after_flush(session, flush_context):
    changed_objects = session.new.union(session.dirty)
    for o in changed_objects:
        trigger_attribute_change_events(o)

event.listen(session, "after_flush", on_after_flush)

The `registry` is a dictionary whose keys are MapperProperty's and whose values are event handlers. `session`, `event`, `inspect`, and `object_mapper` are all sqlalchemy classes and functions.

Problem

The issue is that I can't figure out how to use SQLAlchemy to notify me when an object goes into a new state. I'm using SQLAlchemy ORM (Declarative) to update an object: ``` class Customer(declarative_base()): __table_name__ = "customer" id = Column(Integer, primary_key=True) status = Column(String) ``` I want to know when an object enters a state. Particularly after an UPDATE has been issued and when the state changes. E.g. `Customer.status == 'registered'` and it previously had a different state. I'm currently doing this with an `'set'` attribute event: ``` from sqlalchemy import event from model import Customer def on_set_attribute(target, value, oldvalue, initiator): print target.status print value print oldvalue event.listen( Customer.status, 'set', on_set_attribute, propagate=True, active_history=True) ``` My code fires every time 'set' is called on that attribute, and I check if the `value` and the `oldvalue` are different. The problem is that the `target` parameter isn't fully formed so it doesn't have all the attribute values populated yet. Is there a better way to do this? Thanks!

Original source