Commits

Mike Bayer  committed 72451ee

- When an open Session is garbage collected, the objects
within it which remain are considered detached again
when they are add()-ed to a new Session.
This is accomplished by an extra check that the previous
"session_key" doesn't actually exist among the pool
of Sessions. [ticket:2281]

  • Participants
  • Parent commits 63a5f60

Comments (0)

Files changed (3)

     are constructed after existing ones have been used
     already.
 
+  - When an open Session is garbage collected, the objects
+    within it which remain are considered detached again
+    when they are add()-ed to a new Session.
+    This is accomplished by an extra check that the previous
+    "session_key" doesn't actually exist among the pool
+    of Sessions.  [ticket:2281]
+
   - New declarative features:
         - __declare_last__() method, establishes an event
         listener for the class method that will be called

File lib/sqlalchemy/orm/session.py

                     "present in this session."
                     % (mapperutil.state_str(state), state.key))
 
-        if state.session_id and state.session_id is not self.hash_key:
+        if state.session_id and \
+                state.session_id is not self.hash_key and \
+                state.session_id in _sessions:
             raise sa_exc.InvalidRequestError(
                 "Object '%s' is already attached to session '%s' "
                 "(this is '%s')" % (mapperutil.state_str(state),

File test/orm/test_session.py

                 del u3
                 gc_collect()
 
+    def test_auto_detach_on_gc_session(self):
+        users, User = self.tables.users, self.classes.User
+
+        mapper(User, users)
+
+        sess = Session()
+
+        u1 = User(name='u1')
+        sess.add(u1)
+        sess.commit()
+
+        # can't add u1 to Session,
+        # already belongs to u2
+        s2 = Session()
+        assert_raises_message(
+            sa.exc.InvalidRequestError,
+            r".*is already attached to session",
+            s2.add, u1
+        )
+
+        # garbage collect sess
+        del sess
+        gc_collect()
+
+        # s2 lets it in now despite u1 having
+        # session_key
+        s2.add(u1)
+        assert u1 in s2
+
 class SessionDataTest(_fixtures.FixtureTest):
     def test_expunge_cascade(self):
         Address, addresses, users, User = (self.classes.Address,