]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug in mutable extension as well as
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 19 Mar 2014 15:09:38 +0000 (11:09 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 19 Mar 2014 15:09:38 +0000 (11:09 -0400)
:func:`.attributes.flag_modified` where the change event would not be
propagated if the attribute had been reassigned to itself.
fixes #2997

doc/build/changelog/changelog_08.rst
lib/sqlalchemy/ext/mutable.py
lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/state.py
test/ext/test_mutable.py
test/orm/test_attributes.py

index 08e44a0fdb9f343c61e69ca958b12e63b8c741d3..b88a53a8813c76195991708f058ac0f309f3a9ba 100644 (file)
 .. changelog::
     :version: 0.8.6
 
+    .. change::
+        :tags: bug, ext
+        :versions: 0.9.4
+        :tickets: 2997
+
+        Fixed bug in mutable extension as well as
+        :func:`.attributes.flag_modified` where the change event would not be
+        propagated if the attribute had been reassigned to itself.
+
     .. change::
         :tags: bug, orm
         :versions: 0.9.4
index 4e574935d8c33cc36fc52d18bea5557aa960dc34..7869e888cc11c177234b5889c95597e790fd08eb 100644 (file)
@@ -437,6 +437,9 @@ class MutableBase(object):
             outgoing.
 
             """
+            if value is oldvalue:
+                return value
+
             if not isinstance(value, cls):
                 value = cls.coerce(key, value)
             if value is not None:
index 7647bf9d0722f630933a4a1ca4e3e00d2610a79a..3a786c73d418430c90e4b29bec6e08cf074d26c9 100644 (file)
@@ -1538,4 +1538,4 @@ def flag_modified(instance, key):
     """
     state, dict_ = instance_state(instance), instance_dict(instance)
     impl = state.manager[key].impl
-    state._modified_event(dict_, impl, NO_VALUE)
+    state._modified_event(dict_, impl, NO_VALUE, force=True)
index 9712dd055a1ddcc9ae74bdeb2493950bee330afc..fb5db1fc99168ba3791e1200c670b737094757f6 100644 (file)
@@ -414,10 +414,10 @@ class InstanceState(interfaces._InspectionAttr):
     def _instance_dict(self):
         return None
 
-    def _modified_event(self, dict_, attr, previous, collection=False):
+    def _modified_event(self, dict_, attr, previous, collection=False, force=False):
         if not attr.send_modified_events:
             return
-        if attr.key not in self.committed_state:
+        if attr.key not in self.committed_state or force:
             if collection:
                 if previous is NEVER_SET:
                     if attr.key in dict_:
index ee1b8075e94f2e4607d1887c3fb247ae742e08b5..39cfa7ac518868643dd273fbe17225518079f111 100644 (file)
@@ -96,6 +96,17 @@ class _MutableDictTestBase(object):
         sess.commit()
         eq_(f1.data, {'b': 'c'})
 
+    def test_replace_itself_still_ok(self):
+        sess = Session()
+        f1 = Foo(data={'a': 'b'})
+        sess.add(f1)
+        sess.flush()
+
+        f1.data = f1.data
+        f1.data['b'] = 'c'
+        sess.commit()
+        eq_(f1.data, {'a': 'b', 'b': 'c'})
+
     def test_pickle_parent(self):
         sess = Session()
 
index 4f8092f31cffc316b1b6156b46eceb381db3c607..a7c67517521bf3403455c2181c4cc411aeba5e8c 100644 (file)
@@ -1852,6 +1852,21 @@ class HistoryTest(fixtures.TestBase):
         f.someattr = ['a']
         eq_(self._someattr_history(f), ([['a']], (), ()))
 
+    def test_scalar_inplace_mutation_replace_self_flag_modified_set(self):
+        Foo = self._fixture(uselist=False, useobject=False,
+                                active_history=False)
+        f = Foo()
+        f.someattr = {'a': 'b'}
+        self._commit_someattr(f)
+        eq_(self._someattr_history(f), ((), [{'a': 'b'}], ()))
+
+        # set the attribute to itself; this places a copy
+        # in committed_state
+        f.someattr = f.someattr
+
+        attributes.flag_modified(f, 'someattr')
+        eq_(self._someattr_history(f), ([{'a': 'b'}], (), ()))
+
 
     def test_use_object_init(self):
         Foo, Bar = self._two_obj_fixture(uselist=False)