--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 4489
+
+ Fixed fairly simple but critical issue where the
+ :meth:`.SessionEvents.pending_to_persistent` event would be invoked for
+ objects not just when they move from pending to persistent, but when they
+ were also already persistent and just being updated, thus causing the event
+ to be invoked for all objects on every update.
states, self, to_transient=to_transient
)
- def _register_newly_persistent(self, states):
+ def _register_persistent(self, states):
+ """Register all persistent objects from a flush.
+
+ This is used both for pending objects moving to the persistent
+ state as well as already persistent objects.
+
+ """
+
pending_to_persistent = self.dispatch.pending_to_persistent or None
for state in states:
mapper = _state_mapper(state)
)
state.key = instance_key
+ # there can be an existing state in the identity map
+ # that is replaced when the primary keys of two instances
+ # are swapped; see test/orm/test_naturalpks.py -> test_reverse
self.identity_map.replace(state)
state._orphaned_outside_of_session = False
self._register_altered(states)
if pending_to_persistent is not None:
- for state in states:
+ for state in states.intersection(self._new):
pending_to_persistent(self, state.obj())
# remove from new last, might be the last strong ref
if isdel:
self.session._remove_newly_deleted(isdel)
if other:
- self.session._register_newly_persistent(other)
+ self.session._register_persistent(other)
class IterateMappersMixin(object):
[call.pending_to_persistent(sess, u1), call.flag_checked(u1)],
)
+ u1.name = 'u2'
+ sess.flush()
+
+ # event was not called again
+ eq_(
+ listener.mock_calls,
+ [call.pending_to_persistent(sess, u1), call.flag_checked(u1)],
+ )
+
def test_pending_to_persistent_del(self):
sess, User, start_events = self._fixture()