]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
self-referential working to a small degree
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 1 Apr 2010 17:19:14 +0000 (13:19 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 1 Apr 2010 17:19:14 +0000 (13:19 -0400)
lib/sqlalchemy/orm/dependency.py
lib/sqlalchemy/orm/unitofwork.py
test/orm/test_unitofworkv2.py

index b7c62a48707b886c85c0f09e560b0ed87d88e116..1c6473375e886e425aa724f697a6b342a7d27a8c 100644 (file)
@@ -205,11 +205,11 @@ class OneToManyDP(DependencyProcessor):
             assert parent_saves in uow.cycles
             assert child_saves in uow.cycles
         
-        added, updated, deleted = uow.get_attribute_history(state, self.key, passive=True)
+        added, unchanged, deleted = uow.get_attribute_history(state, self.key, passive=True)
         if not added and not unchanged and not deleted:
             return
         
-        save_parent = unitofwork.SaveUpdateState(state)
+        save_parent = unitofwork.SaveUpdateState(uow, state)
         after_save = unitofwork.ProcessState(uow, self, False, state)
 
         for child_state in added + unchanged + deleted:
@@ -218,9 +218,9 @@ class OneToManyDP(DependencyProcessor):
             
             (deleted, listonly) = uow.states[child_state]
             if deleted:
-                child_action = unitofwork.DeleteState(child_state)
+                child_action = unitofwork.DeleteState(uow, child_state)
             else:
-                child_action = unitofwork.SaveUpdateState(child_state)
+                child_action = unitofwork.SaveUpdateState(uow, child_state)
             
             uow.dependencies.update([
                 (save_parent, after_save),
@@ -234,12 +234,12 @@ class OneToManyDP(DependencyProcessor):
             assert parent_deletes in uow.cycles
             assert child_deletes in uow.cycles
 
-        added, updated, deleted = uow.get_attribute_history(state, self.key, passive=True)
+        added, unchanged, deleted = uow.get_attribute_history(state, self.key, passive=True)
         if not added and not unchanged and not deleted:
             return
 
-        delete_parent = unitofwork.DeleteState(state)
-        after_delete = unitofwork.ProcessState(uow, self, True, state)
+        delete_parent = unitofwork.DeleteState(uow, state)
+        before_delete = unitofwork.ProcessState(uow, self, True, state)
 
         for child_state in added + unchanged + deleted:
             if child_state is None:
@@ -247,14 +247,13 @@ class OneToManyDP(DependencyProcessor):
 
             (deleted, listonly) = uow.states[child_state]
             if deleted:
-                child_action = unitofwork.DeleteState(child_state)
+                child_action = unitofwork.DeleteState(uow, child_state)
             else:
-                child_action = unitofwork.SaveUpdateState(child_state)
+                child_action = unitofwork.SaveUpdateState(uow, child_state)
 
             uow.dependencies.update([
-                (child_action, )
-                (save_parent, after_save),
-                (after_save, child_action),
+                (child_action, before_delete),
+                (before_delete, delete_parent),
             ])
         
         
index 65d85a37b0407bb4e6d262f2d1664467b2223af1..d2f5946df515868de0ad8c01cca296790bb1da3d 100644 (file)
@@ -167,20 +167,30 @@ class UOWTransaction(object):
                 break
         
         self.cycles = cycles = topological.find_cycles(self.dependencies, self.postsort_actions.values())
-        assert not cycles
-        for rec in cycles:
-            rec.per_state_flush_actions(self)
+
+        if cycles:
+            convert = {}
+            for rec in cycles:
+                convert[rec] = set(rec.per_state_flush_actions(self))
 
         for edge in list(self.dependencies):
-            # both nodes in this edge were part of a cycle.
-            # remove that from our deps as it has replaced
-            # itself with per-state actions
+            # remove old dependencies between two cycle nodes,
+            # splice dependencies for dependencies from/to cycle 
+            # nodes from non-cycle nodes
             if cycles.issuperset(edge):
                 self.dependencies.remove(edge)
+            elif edge[0] in cycles:
+                for dep in convert[edge[0]]:
+                    self.dependencies.add((dep, edge[1]))
+            elif edge[1] in cycles:
+                for dep in convert[edge[1]]:
+                    self.dependencies.add((edge[0], dep))
             
         sort = topological.sort(self.dependencies, self.postsort_actions.values())
         print sort
         for rec in sort:
+            if rec in cycles:
+                continue
             rec.execute(self)
             
 
@@ -286,10 +296,11 @@ class ProcessAll(PropertyRecMixin, PostSortRec):
     def per_state_flush_actions(self, uow):
         for state in self._elements(uow):
             if self.delete:
-                self.dependency_processor.per_deleted_state_flush_actions(uow, self.dependency_processor, state)
+                self.dependency_processor.per_deleted_state_flush_actions(uow, state)
             else:
-                self.dependency_processor.per_saved_state_flush_actions(uow, self.dependency_processor, state)
-
+                self.dependency_processor.per_saved_state_flush_actions(uow, state)
+        return iter([])
+        
 class SaveUpdateAll(PostSortRec):
     def __init__(self, uow, mapper):
         self.mapper = mapper
@@ -302,7 +313,7 @@ class SaveUpdateAll(PostSortRec):
     
     def per_state_flush_actions(self, uow):
         for state in uow.states_for_mapper_hierarchy(self.mapper, False, False):
-            SaveUpdateState(uow, state)
+            yield SaveUpdateState(uow, state)
         
 class DeleteAll(PostSortRec):
     def __init__(self, uow, mapper):
@@ -316,20 +327,39 @@ class DeleteAll(PostSortRec):
 
     def per_state_flush_actions(self, uow):
         for state in uow.states_for_mapper_hierarchy(self.mapper, True, False):
-            DeleteState(uow, state)
+            yield DeleteState(uow, state)
 
 class ProcessState(PostSortRec):
     def __init__(self, uow, dependency_processor, delete, state):
         self.dependency_processor = dependency_processor
         self.delete = delete
         self.state = state
+
+    def execute(self, uow):
+        if self.delete:
+            self.dependency_processor.process_deletes(uow, [self.state])
+        else:
+            self.dependency_processor.process_saves(uow, [self.state])
         
 class SaveUpdateState(PostSortRec):
     def __init__(self, uow, state):
         self.state = state
 
+    def execute(self, uow):
+        mapper = self.state.manager.mapper.base_mapper
+        mapper._save_obj(
+            [self.state],
+            uow
+        )
+
 class DeleteState(PostSortRec):
     def __init__(self, uow, state):
         self.state = state
 
+    def execute(self, uow):
+        mapper = self.state.manager.mapper.base_mapper
+        mapper._delete_obj(
+            [self.state],
+            uow
+        )
 
index 15b481898ebb7b0106744abddba6cec1487373cf..8a39e77ee41d9a7e180d36453fd4d2ea335094e9 100644 (file)
@@ -195,7 +195,7 @@ class RudimentaryFlushTest(UOWTest):
         )
 
 class SingleCycleTest(UOWTest):
-    def test_one_to_many(self):
+    def test_one_to_many_save(self):
         mapper(Node, nodes, properties={
             'children':relationship(Node)
         })
@@ -209,36 +209,45 @@ class SingleCycleTest(UOWTest):
         self.assert_sql_execution(
                 testing.db,
                 sess.flush,
+                
                 CompiledSQL(
-                    "INSERT INTO nodes (data) VALUES (:data)",
-                    {'data': 'n1'} 
+                    "INSERT INTO nodes (parent_id, data) VALUES (:parent_id, :data)",
+                    {'parent_id': None, 'data': 'n1'}
                 ),
+                AllOf(
                 CompiledSQL(
-                    "INSERT INTO addresses (user_id, email_address) "
-                    "VALUES (:user_id, :email_address)",
-                    lambda ctx: {'email_address': 'a1', 'user_id':u1.id} 
+                    "INSERT INTO nodes (parent_id, data) VALUES (:parent_id, :data)",
+                    lambda ctx: {'parent_id': n1.id, 'data': 'n2'}
                 ),
                 CompiledSQL(
-                    "INSERT INTO addresses (user_id, email_address) "
-                    "VALUES (:user_id, :email_address)",
-                    lambda ctx: {'email_address': 'a2', 'user_id':u1.id} 
+                    "INSERT INTO nodes (parent_id, data) VALUES (:parent_id, :data)",
+                    lambda ctx: {'parent_id': n1.id, 'data': 'n3'}
                 ),
+                )
             )
+
+    def test_one_to_many_delete_all(self):
+        mapper(Node, nodes, properties={
+            'children':relationship(Node)
+        })
+        sess = create_session()
+
+        n2, n3 = Node(data='n2', children=[]), Node(data='n3', children=[])
+        n1 = Node(data='n1', children=[n2, n3])
+
+        sess.add(n1)
+        sess.flush()
         
-        return
         sess.delete(n1)
         sess.delete(n2)
         sess.delete(n3)
         self.assert_sql_execution(
                 testing.db,
                 sess.flush,
-                CompiledSQL(
-                    "DELETE FROM addresses WHERE addresses.id = :id",
-                    [{'id':a1.id},{'id':a2.id}]
-                ),
-                CompiledSQL(
-                    "DELETE FROM users WHERE users.id = :id",
-                    {'id':u1.id}
+                AllOf(
+                    CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", {'id':3}),
+                    CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", {'id':2}),
                 ),
+                CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", {'id':1})
         )