From: Mike Bayer Date: Wed, 19 Mar 2014 15:09:38 +0000 (-0400) Subject: - Fixed bug in mutable extension as well as X-Git-Tag: rel_0_8_6~12 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=66d090be9cf0524d10568183bc53b2c100000a00;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - 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. fixes #2997 Conflicts: lib/sqlalchemy/orm/state.py test/orm/test_attributes.py --- diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index a0df6a2879..809b389501 100644 --- a/doc/build/changelog/changelog_08.rst +++ b/doc/build/changelog/changelog_08.rst @@ -11,6 +11,15 @@ .. 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 diff --git a/lib/sqlalchemy/ext/mutable.py b/lib/sqlalchemy/ext/mutable.py index 293258b2cc..d5acb18710 100644 --- a/lib/sqlalchemy/ext/mutable.py +++ b/lib/sqlalchemy/ext/mutable.py @@ -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: diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index aaca9e927d..cbbdb477b8 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -1555,4 +1555,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) diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index bbe5bf56f4..4a00cfe9db 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -427,8 +427,8 @@ class InstanceState(interfaces._InspectionAttr): def _instance_dict(self): return None - def _modified_event(self, dict_, attr, previous, collection=False): - if attr.key not in self.committed_state: + def _modified_event(self, dict_, attr, previous, collection=False, force=False): + if attr.key not in self.committed_state or force: if collection: if previous is NEVER_SET: if attr.key in dict_: diff --git a/test/ext/test_mutable.py b/test/ext/test_mutable.py index bda9e5382e..268af6e49b 100644 --- a/test/ext/test_mutable.py +++ b/test/ext/test_mutable.py @@ -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() diff --git a/test/orm/test_attributes.py b/test/orm/test_attributes.py index dbe82f5851..f8cb1f75d9 100644 --- a/test/orm/test_attributes.py +++ b/test/orm/test_attributes.py @@ -1850,6 +1850,23 @@ 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) f = Foo()