]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
this version passes one to many tests so far
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 1 Apr 2010 20:00:57 +0000 (16:00 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 1 Apr 2010 20:00:57 +0000 (16:00 -0400)
lib/sqlalchemy/orm/dependency.py
lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/unitofwork.py
test/orm/test_unitofworkv2.py

index 1c6473375e886e425aa724f697a6b342a7d27a8c..1823b339966891fd84bde709304aefc4917e7d9d 100644 (file)
@@ -168,6 +168,7 @@ class OneToManyDP(DependencyProcessor):
     
     def per_property_flush_actions(self, uow):
         unitofwork.GetDependentObjects(uow, self, False, True)
+        unitofwork.GetDependentObjects(uow, self, True, True)
         
         after_save = unitofwork.ProcessAll(uow, self, False, True)
         before_delete = unitofwork.ProcessAll(uow, self, True, True)
@@ -186,76 +187,73 @@ class OneToManyDP(DependencyProcessor):
                 (before_delete, child_deletes),
             ])
         else:
-            unitofwork.GetDependentObjects(uow, self, True, True)
-            
             uow.dependencies.update([
                 (parent_saves, after_save),
                 (after_save, child_saves),
-                
+                (after_save, child_deletes),
+
                 (child_saves, parent_deletes),
+                (child_deletes, parent_deletes),
+                
                 (before_delete, child_saves),
+                (before_delete, child_deletes),
                 
-                (child_deletes, parent_deletes)
             ])
-    
-    def per_saved_state_flush_actions(self, uow, state):
+
+    def per_state_flush_actions(self, uow, state, isdelete):
         if True:
-            parent_saves = unitofwork.SaveUpdateAll(uow, self.parent)
-            child_saves = unitofwork.SaveUpdateAll(uow, self.mapper)
-            assert parent_saves in uow.cycles
-            assert child_saves in uow.cycles
+            if not isdelete:
+                parent_saves = unitofwork.SaveUpdateAll(uow, self.parent)
+                child_saves = unitofwork.SaveUpdateAll(uow, self.mapper)
+                assert parent_saves in uow.cycles
+                assert child_saves in uow.cycles
+            else:
+                parent_deletes = unitofwork.DeleteAll(uow, self.parent)
+                child_deletes = unitofwork.DeleteAll(uow, self.mapper)
+                assert parent_deletes in uow.cycles
+                assert child_deletes in uow.cycles
+
+        after_save = unitofwork.ProcessAll(uow, self, False, True)
+        before_delete = unitofwork.ProcessAll(uow, self, True, True)
+        after_save.disabled = True
+        before_delete.disabled = True
         
-        added, unchanged, deleted = uow.get_attribute_history(state, self.key, passive=True)
-        if not added and not unchanged and not deleted:
+        sum_ = uow.get_attribute_history(state, self.key, passive=True).sum()
+        if not sum_:
             return
-        
-        save_parent = unitofwork.SaveUpdateState(uow, state)
-        after_save = unitofwork.ProcessState(uow, self, False, state)
 
-        for child_state in added + unchanged + deleted:
+        if not isdelete:
+            save_parent = unitofwork.SaveUpdateState(uow, state)
+            after_save = unitofwork.ProcessState(uow, self, False, state)
+            yield after_save
+        else:
+            delete_parent = unitofwork.DeleteState(uow, state)
+            before_delete = unitofwork.ProcessState(uow, self, True, state)
+            yield before_delete
+            
+        for child_state in sum_:
             if child_state is None:
                 continue
-            
+        
             (deleted, listonly) = uow.states[child_state]
             if deleted:
                 child_action = unitofwork.DeleteState(uow, child_state)
             else:
                 child_action = unitofwork.SaveUpdateState(uow, child_state)
             
-            uow.dependencies.update([
-                (save_parent, after_save),
-                (after_save, child_action),
-            ])
-
-    def per_deleted_state_flush_actions(self, uow, state):
-        if True:
-            parent_deletes = unitofwork.DeleteAll(uow, self.parent)
-            child_deletes = unitofwork.DeleteAll(uow, self.mapper)
-            assert parent_deletes in uow.cycles
-            assert child_deletes in uow.cycles
-
-        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(uow, state)
-        before_delete = unitofwork.ProcessState(uow, self, True, state)
-
-        for child_state in added + unchanged + deleted:
-            if child_state is None:
-                continue
-
-            (deleted, listonly) = uow.states[child_state]
-            if deleted:
-                child_action = unitofwork.DeleteState(uow, child_state)
+            if not isdelete:
+                uow.dependencies.update([
+                    (save_parent, after_save),
+                    (after_save, child_action),
+                    (save_parent, child_action)
+                ])
             else:
-                child_action = unitofwork.SaveUpdateState(uow, child_state)
-
-            uow.dependencies.update([
-                (child_action, before_delete),
-                (before_delete, delete_parent),
-            ])
-        
+                uow.dependencies.update([
+                    (child_action, before_delete),
+                    (before_delete, delete_parent),
+                    (child_action, delete_parent)
+                ])
+                
         
     def presort_deletes(self, uowcommit, states):
         # head object is being deleted, and we manage its list of child objects
index 07f6a09aba16c8c7525c13c80aa225b7dcb42fcc..56291570565b7e72d7cc9ef8bf534307640b4279 100644 (file)
@@ -506,6 +506,9 @@ class MapperProperty(object):
 
     def per_property_flush_actions(self, uow):
         pass
+
+    def per_state_flush_actions(self, uow, state, isdelete):
+        return iter([])
         
     def is_primary(self):
         """Return True if this ``MapperProperty``'s mapper is the
index c95bcd4c8770e06696dd026478853f011f381db4..9a18bd4cf62376cf24f36b16f97628db5f793c63 100644 (file)
@@ -1261,6 +1261,18 @@ class Mapper(object):
         
         for prop in self._props.values():
             prop.per_property_flush_actions(uow)
+    
+    def per_state_flush_actions(self, uow, state, isdelete):
+        if isdelete:
+            action = unitofwork.DeleteState(uow, state)
+        else:
+            action = unitofwork.SaveUpdateState(uow, state)
+        
+        yield action
+        for prop in self._props.values():
+            for rec in prop.per_state_flush_actions(uow, state, isdelete):
+                yield rec
+        
         
     def _save_obj(self, states, uowtransaction, postupdate=False, 
                                 post_update_cols=None, single=False):
index 61dc9eb55f474874348c6cc7013305d7c91d5cfe..754cdc118f0b1fda4c55b5b56073f0503711db3c 100644 (file)
@@ -1207,6 +1207,11 @@ class RelationshipProperty(StrategizedProperty):
         if not self.viewonly and self._dependency_processor:
             self._dependency_processor.per_property_flush_actions(uow)
 
+    def per_state_flush_actions(self, uow, state, isdelete):
+        if not self.viewonly and self._dependency_processor:
+            for rec in self._dependency_processor.per_state_flush_actions(uow, state, isdelete):
+                yield rec
+
     def _create_joins(self, source_polymorphic=False, 
                             source_selectable=None, dest_polymorphic=False, 
                             dest_selectable=None, of_type=None):
index d2f5946df515868de0ad8c01cca296790bb1da3d..7bfac95b63a92378922e943201fdc099c783c33f 100644 (file)
@@ -158,6 +158,9 @@ class UOWTransaction(object):
                 
     def execute(self):
         
+        # execute presort_actions, until all states
+        # have been processed.   a presort_action might
+        # add new states to the uow.
         while True:
             ret = False
             for action in self.presort_actions.values():
@@ -166,31 +169,44 @@ class UOWTransaction(object):
             if not ret:
                 break
         
+        # see if the graph of mapper dependencies has cycles.
         self.cycles = cycles = topological.find_cycles(self.dependencies, self.postsort_actions.values())
-
+        
         if cycles:
-            convert = {}
-            for rec in cycles:
-                convert[rec] = set(rec.per_state_flush_actions(self))
-
-        for edge in list(self.dependencies):
-            # 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))
-            
+            # if yes, break the per-mapper actions into
+            # per-state actions
+            convert = dict(
+                (rec, set(rec.per_state_flush_actions(self)))
+                for rec in cycles
+            )
+
+            # rewrite the existing dependencies to point to
+            # the per-state actions for those per-mapper actions
+            # that were broken up.
+            for edge in list(self.dependencies):
+                if cycles.issuperset(edge):
+                    self.dependencies.remove(edge)
+                elif edge[0] in cycles:
+                    self.dependencies.remove(edge)
+                    for dep in convert[edge[0]]:
+                        self.dependencies.add((dep, edge[1]))
+                elif edge[1] in cycles:
+                    self.dependencies.remove(edge)
+                    for dep in convert[edge[1]]:
+                        self.dependencies.add((edge[0], dep))
+        
+            # remove actions that were part of the cycles,
+            # or have been marked as "disabled" by the "breaking up"
+            # process
+            for k, v in list(self.postsort_actions.items()):
+                if v.disabled or v in cycles:
+                    del self.postsort_actions[k]
+        
+        # execute actions
         sort = topological.sort(self.dependencies, self.postsort_actions.values())
-        print sort
+        #print self.dependencies
+        #print sort
         for rec in sort:
-            if rec in cycles:
-                continue
             rec.execute(self)
             
 
@@ -219,6 +235,8 @@ class PreSortRec(object):
             return ret
 
 class PostSortRec(object):
+    disabled = False
+    
     def __new__(cls, uow, *args):
         key = (cls, ) + args
         if key in uow.postsort_actions:
@@ -294,11 +312,6 @@ class ProcessAll(PropertyRecMixin, PostSortRec):
             self.dependency_processor.process_saves(uow, states)
 
     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, state)
-            else:
-                self.dependency_processor.per_saved_state_flush_actions(uow, state)
         return iter([])
         
 class SaveUpdateAll(PostSortRec):
@@ -313,7 +326,8 @@ class SaveUpdateAll(PostSortRec):
     
     def per_state_flush_actions(self, uow):
         for state in uow.states_for_mapper_hierarchy(self.mapper, False, False):
-            yield SaveUpdateState(uow, state)
+            for rec in self.mapper.per_state_flush_actions(uow, state, False):
+                yield rec
         
 class DeleteAll(PostSortRec):
     def __init__(self, uow, mapper):
@@ -327,7 +341,8 @@ class DeleteAll(PostSortRec):
 
     def per_state_flush_actions(self, uow):
         for state in uow.states_for_mapper_hierarchy(self.mapper, True, False):
-            yield DeleteState(uow, state)
+            for rec in self.mapper.per_state_flush_actions(uow, state, True):
+                yield rec
 
 class ProcessState(PostSortRec):
     def __init__(self, uow, dependency_processor, delete, state):
@@ -352,6 +367,12 @@ class SaveUpdateState(PostSortRec):
             uow
         )
 
+    def __repr__(self):
+        return "%s(%s)" % (
+            self.__class__.__name__,
+            mapperutil.state_str(self.state)
+        )
+
 class DeleteState(PostSortRec):
     def __init__(self, uow, state):
         self.state = state
@@ -363,3 +384,9 @@ class DeleteState(PostSortRec):
             uow
         )
 
+    def __repr__(self):
+        return "%s(%s)" % (
+            self.__class__.__name__,
+            mapperutil.state_str(self.state)
+        )
+
index 8a39e77ee41d9a7e180d36453fd4d2ea335094e9..4ccc3df689012dcae4660012e9584b2052561cdf 100644 (file)
@@ -89,11 +89,11 @@ class RudimentaryFlushTest(UOWTest):
                 sess.flush,
                 CompiledSQL(
                     "UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
-                    [{u'addresses_id': 1, 'user_id': None}]
+                    lambda ctx: [{u'addresses_id': a1.id, 'user_id': None}]
                 ),
                 CompiledSQL(
                     "UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
-                    [{u'addresses_id': 2, 'user_id': None}]
+                    lambda ctx: [{u'addresses_id': a2.id, 'user_id': None}]
                 ),
                 CompiledSQL(
                     "DELETE FROM users WHERE users.id = :id",
@@ -182,11 +182,11 @@ class RudimentaryFlushTest(UOWTest):
                 sess.flush,
                 CompiledSQL(
                     "UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
-                    [{u'addresses_id': 1, 'user_id': None}]
+                    lambda ctx: [{u'addresses_id': a1.id, 'user_id': None}]
                 ),
                 CompiledSQL(
                     "UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
-                    [{u'addresses_id': 2, 'user_id': None}]
+                    lambda ctx: [{u'addresses_id': a2.id, 'user_id': None}]
                 ),
                 CompiledSQL(
                     "DELETE FROM users WHERE users.id = :id",