问题
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!
回答1:
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.
回答2:
use the before_update event or the before_flush event to intercept this happening at a later point in time.
来源:https://stackoverflow.com/questions/15642286/how-can-i-get-a-sqlalchemy-orm-objects-previous-state-after-a-db-update