if self._enable_transaction_accounting and self.transaction:
self.transaction._deleted[state] = True
+ if persistent_to_deleted is not None:
+ # get a strong reference before we pop out of
+ # self._deleted
+ obj = state.obj()
+
self.identity_map.safe_discard(state)
self._deleted.pop(state, None)
state._deleted = True
# is still in the transaction snapshot and needs to be
# tracked as part of that
if persistent_to_deleted is not None:
- persistent_to_deleted(self, state.obj())
+ persistent_to_deleted(self, obj)
def add(self, instance, _warn=True):
"""Place an object in the ``Session``.
)
obj = state.obj()
+
+ # check for late gc
+ if obj is None:
+ return
+
to_attach = self._before_attach(state, obj)
self._deleted.pop(state, None)
if persistent:
if to_transient:
if persistent_to_transient is not None:
- persistent_to_transient(session, state.obj())
+ obj = state.obj()
+ if obj is not None:
+ persistent_to_transient(session, obj)
elif persistent_to_detached is not None:
- persistent_to_detached(session, state.obj())
+ obj = state.obj()
+ if obj is not None:
+ persistent_to_detached(session, obj)
elif deleted and deleted_to_detached is not None:
- deleted_to_detached(session, state.obj())
+ obj = state.obj()
+ if obj is not None:
+ deleted_to_detached(session, obj)
elif pending and pending_to_transient is not None:
- pending_to_transient(session, state.obj())
+ obj = state.obj()
+ if obj is not None:
+ pending_to_transient(session, obj)
state._strong_obj = None
Session, sessionmaker, attributes, configure_mappers
from sqlalchemy.orm.instrumentation import ClassManager
from sqlalchemy.orm import instrumentation, events
-from sqlalchemy.testing import eq_
+from sqlalchemy.testing import eq_, is_not_
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import AssertsCompiledSQL
from sqlalchemy.testing.util import gc_collect
]
)
+ def test_pending_to_persistent_del(self):
+ sess, User, start_events = self._fixture()
+
+ @event.listens_for(sess, "pending_to_persistent")
+ def pending_to_persistent(session, instance):
+ listener.flag_checked(instance)
+ # this is actually u1, because
+ # we have a strong ref internally
+ is_not_(None, instance)
+
+ u1 = User(name='u1')
+ sess.add(u1)
+
+ u1_inst_state = u1._sa_instance_state
+ del u1
+
+ gc_collect()
+
+ listener = start_events()
+
+ sess.flush()
+
+ eq_(
+ listener.mock_calls,
+ [
+ call.flag_checked(u1_inst_state.obj()),
+ call.pending_to_persistent(
+ sess, u1_inst_state.obj()),
+ ]
+ )
+
+ def test_persistent_to_deleted_del(self):
+ sess, User, start_events = self._fixture()
+
+ u1 = User(name='u1')
+ sess.add(u1)
+ sess.flush()
+
+ listener = start_events()
+
+ @event.listens_for(sess, "persistent_to_deleted")
+ def persistent_to_deleted(session, instance):
+ is_not_(None, instance)
+ listener.flag_checked(instance)
+
+ sess.delete(u1)
+ u1_inst_state = u1._sa_instance_state
+
+ del u1
+ gc_collect()
+
+ sess.flush()
+
+ eq_(
+ listener.mock_calls,
+ [
+ call.persistent_to_deleted(sess, u1_inst_state.obj()),
+ call.flag_checked(u1_inst_state.obj())
+ ]
+ )
+
+
+
def test_detached_to_persistent(self):
sess, User, start_events = self._fixture()