]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug regarding flushes on self-referential
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 13 Jun 2010 20:25:26 +0000 (16:25 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 13 Jun 2010 20:25:26 +0000 (16:25 -0400)
bi-directional many-to-many relationships, where
two objects made to mutually reference each other
in one flush would fail to insert a row for both
sides.  Regression from 0.5. [ticket:1824]

CHANGES
lib/sqlalchemy/orm/dependency.py
test/orm/test_manytomany.py

diff --git a/CHANGES b/CHANGES
index efd95605de03db506c0ca3e13fc2b4a34663b708..714f82bab172654fa8c0d9c1bd143cedad91be00 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -5,6 +5,13 @@ CHANGES
 =======
 0.6.2
 =====
+- orm
+  - Fixed bug regarding flushes on self-referential 
+    bi-directional many-to-many relationships, where
+    two objects made to mutually reference each other
+    in one flush would fail to insert a row for both
+    sides.  Regression from 0.5. [ticket:1824]
+    
 - sql
   - The warning emitted by the Unicode and String types
     with convert_unicode=True no longer embeds the actual
index b2c3d1fb9d1f0707962173a0fc2abdde1158b5c4..ba2ae8889c2371341c06d3906b6dbdaf0959ed0e 100644 (file)
@@ -870,7 +870,7 @@ class ManyToManyDP(DependencyProcessor):
         secondary_update = []
         
         processed = self._get_reversed_processed_set(uowcommit)
-        
+        tmp = set()
         for state in states:
             history = uowcommit.get_attribute_history(
                                     state, 
@@ -890,8 +890,10 @@ class ManyToManyDP(DependencyProcessor):
                                         False, uowcommit)
                     secondary_delete.append(associationrow)
                 
-                if processed is not None:
-                    processed.update((c, state) for c in history.non_added())
+                tmp.update((c, state) for c in history.non_added())
+
+        if processed is not None:
+            processed.update(tmp)
                 
         self._run_crud(uowcommit, secondary_insert, 
                         secondary_update, secondary_delete)
@@ -902,7 +904,8 @@ class ManyToManyDP(DependencyProcessor):
         secondary_update = []
 
         processed = self._get_reversed_processed_set(uowcommit)
-        
+        tmp = set()
+
         for state in states:
             history = uowcommit.get_attribute_history(state, self.key)
             if history:
@@ -928,8 +931,7 @@ class ManyToManyDP(DependencyProcessor):
                                         False, uowcommit)
                     secondary_delete.append(associationrow)
                 
-                if processed is not None:
-                    processed.update((c, state) for c in history.added + history.deleted)
+                tmp.update((c, state) for c in history.added + history.deleted)
                 
             if not self.passive_updates and \
                     self._pks_changed(uowcommit, state):
@@ -954,7 +956,9 @@ class ManyToManyDP(DependencyProcessor):
 
                     secondary_update.append(associationrow)
                     
-
+        if processed is not None:
+            processed.update(tmp)
+            
         self._run_crud(uowcommit, secondary_insert, 
                         secondary_update, secondary_delete)
         
index cac5fda78147d6feb50700fb7a08574ea42a92ee..46d0cc44fd1a380cbdbdc91d6edf15ed763fa214 100644 (file)
@@ -82,14 +82,15 @@ class M2MTest(_base.MappedTest):
     def test_circular(self):
         """test a many-to-many relationship from a table to itself."""
 
-        Place.mapper = mapper(Place, place)
-
-        Place.mapper.add_property('places', relationship(
-            Place.mapper, secondary=place_place, primaryjoin=place.c.place_id==place_place.c.pl1_id,
-            secondaryjoin=place.c.place_id==place_place.c.pl2_id,
-            order_by=place_place.c.pl2_id,
-            lazy='select',
-            ))
+        mapper(Place, place, properties={
+            'places': relationship(
+                        Place,
+                        secondary=place_place, 
+                        primaryjoin=place.c.place_id==place_place.c.pl1_id,
+                        secondaryjoin=place.c.place_id==place_place.c.pl2_id,
+                        order_by=place_place.c.pl2_id
+                )
+        })
 
         sess = create_session()
         p1 = Place('place1')
@@ -128,6 +129,34 @@ class M2MTest(_base.MappedTest):
         [sess.delete(p) for p in p1,p2,p3,p4,p5,p6,p7]
         sess.flush()
 
+    @testing.resolve_artifact_names
+    def test_circular_mutation(self):
+        """Test that a mutation in a self-ref m2m of both sides succeeds."""
+
+        mapper(Place, place, properties={
+            'child_places': relationship(
+                        Place,
+                        secondary=place_place, 
+                        primaryjoin=place.c.place_id==place_place.c.pl1_id,
+                        secondaryjoin=place.c.place_id==place_place.c.pl2_id,
+                        order_by=place_place.c.pl2_id,
+                        backref='parent_places'
+                )
+        })
+
+        sess = create_session()
+        p1 = Place('place1')
+        p2 = Place('place2')
+        p2.parent_places = [p1]
+        sess.add_all([p1, p2])
+        p1.parent_places.append(p2)
+        sess.flush()
+        
+        sess.expire_all()
+        assert p1 in p2.parent_places
+        assert p2 in p1.parent_places
+        
+
     @testing.resolve_artifact_names
     def test_double(self):
         """test that a mapper can have two eager relationships to the same table, via