]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add test and retroactive changelog for issue 4040
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 1 Oct 2018 18:00:39 +0000 (14:00 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 1 Oct 2018 18:00:39 +0000 (14:00 -0400)
Fixes: #4040
Change-Id: I707c1cd2708a37102ad8184bec21be35cb6242d7

doc/build/changelog/changelog_12.rst
lib/sqlalchemy/orm/session.py
lib/sqlalchemy/orm/unitofwork.py
test/orm/test_cascade.py

index 8e471dfc3ccce6f4d421985b77396cb5a53b0fad..3b6838938dfad9c1f21aa7045e3e259d45ead3f0 100644 (file)
         attributes, which has the effect of the attribute being undeferred
         for the next refesh, causing an unexpected load of the attribute.
 
+    .. change::
+        :tags: bug, orm
+        :tickets: 4040
+
+        Fixed bug involving delete-orphan cascade where a related item
+        that becomes an orphan before the parent object is part of a
+        session is still tracked as moving into orphan status, which results
+        in it being expunged from the session rather than being flushed.
+
+        .. note::  This fix was inadvertently merged during the 1.2.0b3
+           release and was **not added to the changelog** at that time.
+           This changelog note was added to the release retroactively as of
+           version 1.2.13.
+
     .. change::
         :tags: bug, orm
         :tickets: 4026
index 2c7fd86d2cbe27f998702baceaa6cad276b2da59..6c555393981e886f044551aa4e75ed3cd7101a25 100644 (file)
@@ -2316,7 +2316,8 @@ class Session(_SessionClassMethods):
 
             is_persistent_orphan = is_orphan and state.has_identity
 
-            if is_orphan and not is_persistent_orphan and state._orphaned_outside_of_session:
+            if is_orphan and not is_persistent_orphan and \
+                    state._orphaned_outside_of_session:
                 self._expunge_states([state])
             else:
                 _reg = flush_context.register_object(
index 669fed6180ea061743e894a39d87a905fe954f6a..ae5cee8d2e1efa01738c9ddd574ecf3eee7b6525 100644 (file)
@@ -69,6 +69,10 @@ def track_cascade_events(descriptor, prop):
             if sess and item_state in sess._new:
                 sess.expunge(item)
             else:
+                # the related item may or may not itself be in a
+                # Session, however the parent for which we are catching
+                # the event is not in a session, so memoize this on the
+                # item
                 item_state._orphaned_outside_of_session = True
 
     def set_(state, newvalue, oldvalue, initiator):
index 55467db4b9b9b93a21eb6c5c8e8d50eb897da5f8..fecf3dcc3c79a21bc286447fcbc10999a40c5125 100644 (file)
@@ -272,6 +272,30 @@ class O2MCascadeDeleteOrphanTest(fixtures.MappedTest):
         u.orders.remove(o1)
         assert o1 not in sess
 
+    def test_remove_pending_from_pending_parent(self):
+        # test issue #4040
+
+        User, Order = self.classes.User, self.classes.Order
+
+        sess = Session()
+
+        u = User(name='jack')
+
+        o1 = Order()
+        sess.add(o1)
+
+        # object becomes an orphan, but parent is not in session
+        u.orders.append(o1)
+        u.orders.remove(o1)
+
+        sess.add(u)
+
+        assert o1 in sess
+
+        sess.flush()
+
+        assert o1 not in sess
+
     def test_delete(self):
         User, users, orders, Order = (self.classes.User,
                                       self.tables.users,