deleted objects aren't fully detached after transaction

Issue #2658 resolved
Mike Bayer repo owner created an issue

im editing this description a lot. I think when the flush has proceeded but the transaction is still around, the object needs to remain associated with that Session, since it still is.

this test also illustrates a new "was_deleted" function, since we need to expose state.deleted:

from sqlalchemy import Column, Integer
from sqlalchemy.orm import object_session
from sqlalchemy.orm.util import has_identity, was_deleted
import sqlalchemy
import sqlalchemy.ext.declarative
import sqlalchemy.orm

engine = sqlalchemy.create_engine('sqlite:///:memory:')
session_factory = sqlalchemy.orm.scoped_session(
            sqlalchemy.orm.sessionmaker(bind=engine))

Object = sqlalchemy.ext.declarative.declarative_base()

class O(Object):

    __tablename__ = 'object'
    id = Column(Integer, primary_key=True)


def show_object_state(obj):
    if object_session(obj) is not None and was_deleted(obj):
        return "deleted in transaction"
    if object_session(obj) is None and not has_identity(obj):
        return 'transient'
    if object_session(obj) is not None and not has_identity(obj):
        return 'pending'
    if object_session(obj) is None and has_identity(obj):
        return 'detached'
    if object_session(obj) is not None and has_identity(obj):
        return 'persistent'
    raise RuntimeError('unknown state')

Object.metadata.create_all(engine)
session = session_factory()

o = O()
o.id = 1

session.add(o)
session.commit()
session.delete(o)

session.flush()
assert object_session(o) is session  # because it still could be rolled back

assert show_object_state(o) == 'deleted in transaction'

session.rollback()

assert object_session(o) is session   # and its back
assert show_object_state(o) == 'persistent'

session.delete(o)
session.commit()

assert object_session(o) is None
assert show_object_state(o) == 'detached'

patch, also adds util function was_deleted:

diff -r 7790425a993480e81605efc8a07fed13bcdd4841 lib/sqlalchemy/orm/session.py
--- a/lib/sqlalchemy/orm/session.py Mon Jan 28 13:58:01 2013 -0500
+++ b/lib/sqlalchemy/orm/session.py Fri Feb 01 11:28:15 2013 -0500
@@ -255,6 +255,10 @@
         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()
diff -r 7790425a993480e81605efc8a07fed13bcdd4841 lib/sqlalchemy/orm/util.py
--- a/lib/sqlalchemy/orm/util.py    Mon Jan 28 13:58:01 2013 -0500
+++ b/lib/sqlalchemy/orm/util.py    Fri Feb 01 11:28:15 2013 -0500
@@ -1213,6 +1213,9 @@
     state = attributes.instance_state(object)
     return state.has_identity

+def was_deleted(object):
+    state = attributes.instance_state(object)
+    return state.deleted

 def instance_str(instance):
     """Return a string describing an instance."""

Comments (2)

  1. Log in to comment