]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add initiator argument to set_attribute
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 8 Feb 2018 14:16:39 +0000 (09:16 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 8 Feb 2018 22:40:39 +0000 (17:40 -0500)
Added new argument :paramref:`.attributes.set_attribute.inititator`
to the :func:`.attributes.set_attribute` function, allowing an
event token received from a listener function to be propagated
to subsequent set events.

Change-Id: I6ede21e42153026ab46a1d2ec33aa3f999db98e2

doc/build/changelog/unreleased_12/add_initiator.rst [new file with mode: 0644]
lib/sqlalchemy/orm/attributes.py
test/orm/test_attributes.py

diff --git a/doc/build/changelog/unreleased_12/add_initiator.rst b/doc/build/changelog/unreleased_12/add_initiator.rst
new file mode 100644 (file)
index 0000000..a37f9ae
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: feature, orm
+
+    Added new argument :paramref:`.attributes.set_attribute.inititator`
+    to the :func:`.attributes.set_attribute` function, allowing an
+    event token received from a listener function to be propagated
+    to subsequent set events.
index b175297acef39c99092ea6c4f1f935d3ff6929cc..e9227362e79006277be2c172f4ab927f83494421 100644 (file)
@@ -1592,7 +1592,7 @@ def set_committed_value(instance, key, value):
     state.manager[key].impl.set_committed_value(state, dict_, value)
 
 
-def set_attribute(instance, key, value):
+def set_attribute(instance, key, value, initiator=None):
     """Set the value of an attribute, firing history events.
 
     This function may be used regardless of instrumentation
@@ -1601,9 +1601,24 @@ def set_attribute(instance, key, value):
     of this method to establish attribute state as understood
     by SQLAlchemy.
 
+    :param instance: the object that will be modified
+
+    :param key: string name of the attribute
+
+    :param value: value to assign
+
+    :param initiator: an instance of :class:`.Event` that would have
+     been propagated from a previous event listener.  This argument
+     is used when the :func:`.set_attribute` function is being used within
+     an existing event listening function where an :class:`.Event` object
+     is being supplied; the object may be used to track the origin of the
+     chain of events.
+
+     .. versionadded:: 1.2.3
+
     """
     state, dict_ = instance_state(instance), instance_dict(instance)
-    state.manager[key].impl.set(state, dict_, value, None)
+    state.manager[key].impl.set(state, dict_, value, initiator)
 
 
 def get_attribute(instance, key):
index 4646a010ada0cea7ad198f636504e3d35745cc8f..12c9dddb9c2a1afd6256ef49670dbbd143f763ad 100644 (file)
@@ -1014,6 +1014,42 @@ class UtilTest(fixtures.ORMTest):
         attributes.del_attribute(f1, "coll")
         assert "coll" not in f1.__dict__
 
+    def test_initiator_arg(self):
+        class Foo(object):
+            pass
+
+        class Bar(object):
+            pass
+
+        instrumentation.register_class(Foo)
+        instrumentation.register_class(Bar)
+        attributes.register_attribute(
+            Foo, "a", uselist=False, useobject=False)
+        attributes.register_attribute(
+            Bar, "b", uselist=False, useobject=False)
+
+        @event.listens_for(Foo.a, "set")
+        def sync_a(target, value, oldvalue, initiator):
+            parentclass = initiator.parent_token.class_
+            if parentclass is Foo:
+                attributes.set_attribute(target.bar, "b", value, initiator)
+
+        @event.listens_for(Bar.b, "set")
+        def sync_b(target, value, oldvalue, initiator):
+            parentclass = initiator.parent_token.class_
+            if parentclass is Bar:
+                attributes.set_attribute(target.foo, "a", value, initiator)
+
+        f1 = Foo()
+        b1 = Bar()
+        f1.bar = b1
+        b1.foo = f1
+
+        f1.a = 'x'
+        eq_(b1.b, 'x')
+        b1.b = 'y'
+        eq_(f1.a, 'y')
+
 
 class BackrefTest(fixtures.ORMTest):