--- /dev/null
+"""
+Illustrates how to use AttributeExtension to listen for change events.
+
+"""
+
+from sqlalchemy.orm.interfaces import AttributeExtension, InstrumentationManager
+
+class InstallListeners(InstrumentationManager):
+ def instrument_attribute(self, class_, key, inst):
+ """Add an event listener to all InstrumentedAttributes."""
+
+ inst.impl.extensions.append(AttributeListener(key))
+ return super(InstallListeners, self).instrument_attribute(class_, key, inst)
+
+class AttributeListener(AttributeExtension):
+ """Generic event listener.
+
+ Propigates attribute change events to a
+ "receive_change_event()" method on the target
+ instance.
+
+ """
+ def __init__(self, key):
+ self.key = key
+
+ def append(self, state, value, initiator):
+ self._report(state, value, None, "appended")
+
+ def remove(self, state, value, initiator):
+ self._report(state, value, None, "removed")
+
+ def set(self, state, value, oldvalue, initiator):
+ self._report(state, value, oldvalue, "set")
+
+ def _report(self, state, value, oldvalue, verb):
+ state.obj().receive_change_event(verb, self.key, value, oldvalue)
+
+if __name__ == '__main__':
+
+ from sqlalchemy import *
+ from sqlalchemy.orm import *
+ from sqlalchemy.ext.declarative import declarative_base
+
+ class Base(object):
+ __sa_instrumentation_manager__ = InstallListeners
+
+ def receive_change_event(self, verb, key, value, oldvalue):
+ s = "Value '%s' %s on attribute '%s', " % (value, verb, key)
+ if oldvalue:
+ s += "which replaced the value '%s', " % oldvalue
+ s += "on object %s" % self
+ print s
+
+ Base = declarative_base(cls=Base)
+
+ class MyMappedClass(Base):
+ __tablename__ = "mytable"
+
+ id = Column(Integer, primary_key=True)
+ data = Column(String(50))
+ related_id = Column(Integer, ForeignKey("related.id"))
+ related = relation("Related", backref="mapped")
+
+ def __str__(self):
+ return "MyMappedClass(data=%r)" % self.data
+
+ class Related(Base):
+ __tablename__ = "related"
+
+ id = Column(Integer, primary_key=True)
+ data = Column(String(50))
+
+ def __str__(self):
+ return "Related(data=%r)" % self.data
+
+ # classes are instrumented. Demonstrate the events !
+
+ m1 = MyMappedClass(data='m1', related=Related(data='r1'))
+ m1.data = 'm1mod'
+ m1.related.mapped.append(MyMappedClass(data='m2'))
+ del m1.data
+
+
\ No newline at end of file
return l
class AttributeExtension(object):
- """An abstract class which specifies `append`, `delete`, and `set`
- event handlers to be attached to an object property.
+ """An event handler for individual attribute change events.
+
+ AttributeExtension is assembled within the descriptors associated
+ with a mapped class.
+
"""
- def append(self, obj, child, initiator):
+ def append(self, state, value, initiator):
pass
- def remove(self, obj, child, initiator):
+ def remove(self, state, value, initiator):
pass
- def set(self, obj, child, oldchild, initiator):
+ def set(self, state, value, oldvalue, initiator):
pass