.. 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
outgoing.
"""
+ if value is oldvalue:
+ return value
+
if not isinstance(value, cls):
value = cls.coerce(key, value)
if value is not None:
"""
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)
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_:
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()
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)