Commits

Mike Bayer committed 223b697

- Added new helper function :func:`.was_deleted`, returns True
if the given object was the subject of a :meth:`.Session.delete`
operation.
- An object that's deleted from a session will be de-associated with
that session fully after the transaction is committed, that is
the :func:`.object_session` function will return None.
[ticket:2658]

Comments (0)

Files changed (6)

doc/build/changelog/changelog_08.rst

     :version: 0.8.0
 
     .. change::
+        :tags: feature, orm
+        :tickets: 2658
+
+      Added new helper function :func:`.was_deleted`, returns True
+      if the given object was the subject of a :meth:`.Session.delete`
+      operation.
+
+    .. change::
+        :tags: bug, orm
+        :tickets: 2658
+
+      An object that's deleted from a session will be de-associated with
+      that session fully after the transaction is committed, that is
+      the :func:`.object_session` function will return None.
+
+    .. change::
         :tags: bug, oracle
 
       The cx_oracle dialect will no longer run the bind parameter names

doc/build/orm/session.rst

 
 .. autofunction:: object_session
 
+.. autofunction:: was_deleted
+
 Attribute and State Management Utilities
 -----------------------------------------
 

lib/sqlalchemy/orm/__init__.py

      object_mapper,
      outerjoin,
      polymorphic_union,
+     was_deleted,
      with_parent,
      with_polymorphic,
      )
     'undefer',
     'undefer_group',
     'validates',
+    'was_deleted',
     'with_polymorphic'
     )
 

lib/sqlalchemy/orm/session.py

         if not self.nested and self.session.expire_on_commit:
             for s in self.session.identity_map.all_states():
                 s._expire(s.dict, self.session.identity_map._modified)
+            for s in self._deleted:
+                s.session_id = None
+            self._deleted.clear()
+
 
     def _connection_for_bind(self, bind):
         self._assert_is_active()

lib/sqlalchemy/orm/util.py

 
 
 def has_identity(object):
+    """Return True if the given object has a database
+    identity.
+
+    This typically corresponds to the object being
+    in either the persistent or detached state.
+
+    .. seealso::
+
+        :func:`.was_deleted`
+
+    """
     state = attributes.instance_state(object)
     return state.has_identity
 
+def was_deleted(object):
+    """Return True if the given object was deleted
+    within a session flush.
+
+    .. versionadded:: 0.8.0
+
+    """
+
+    state = attributes.instance_state(object)
+    return state.deleted
 
 def instance_str(instance):
     """Return a string describing an instance."""

test/orm/test_session.py

 from sqlalchemy import Integer, String, Sequence
 from sqlalchemy.testing.schema import Table, Column
 from sqlalchemy.orm import mapper, relationship, backref, joinedload, \
-    exc as orm_exc, object_session
+    exc as orm_exc, object_session, was_deleted
 from sqlalchemy.util import pypy
 from sqlalchemy.testing import fixtures
 from test.orm import _fixtures
             assert sa.orm.object_session(a) is None
             assert sa.orm.attributes.instance_state(a).session_id is None
 
+    def test_deleted_expunged(self):
+        users, User = self.tables.users, self.classes.User
+
+        mapper(User, users)
+        sess = Session()
+
+        u1 = sess.query(User).first()
+        sess.delete(u1)
+
+        assert not was_deleted(u1)
+        sess.flush()
+
+        assert was_deleted(u1)
+        assert u1 not in sess
+        assert object_session(u1) is sess
+        sess.commit()
+
+        assert object_session(u1) is None
 
 
 class WeakIdentityMapTest(_fixtures.FixtureTest):