]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- add an example illustrating attribute event reception.
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 29 Aug 2008 16:15:41 +0000 (16:15 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 29 Aug 2008 16:15:41 +0000 (16:15 +0000)
examples/custom_attributes/listen_for_events.py [new file with mode: 0644]
lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/interfaces.py

diff --git a/examples/custom_attributes/listen_for_events.py b/examples/custom_attributes/listen_for_events.py
new file mode 100644 (file)
index 0000000..71f8bba
--- /dev/null
@@ -0,0 +1,83 @@
+"""
+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
index 2d7c726a1f7261bec6f7d36504d5de3fd9886d18..e67026eead65ac829dbab1faa8e74410ed4321a7 100644 (file)
@@ -1545,7 +1545,10 @@ class InstrumentationRegistry(object):
     def state_of(self, instance):
         if instance is None:
             raise AttributeError("None has no persistent state.")
-        return self.state_finders[instance.__class__](instance)
+        try:
+            return self.state_finders[instance.__class__](instance)
+        except KeyError:
+            raise AttributeError("%r is not instrumented" % instance.__class__)
 
     def state_or_default(self, instance, default=None):
         if instance is None:
index ff15062aab8c8b5777c69ebca57f6221e9547a3e..6dd2225c881a16c213eb0dddb9a81090deb05079 100644 (file)
@@ -702,17 +702,20 @@ class PropertyOption(MapperOption):
         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