]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- added some dependency logging
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 29 Sep 2006 23:46:57 +0000 (23:46 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 29 Sep 2006 23:46:57 +0000 (23:46 +0000)
- moved the ClauseSynchronizer compile from properties to dependency where its used

lib/sqlalchemy/orm/dependency.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/topological.py
lib/sqlalchemy/orm/unitofwork.py

index 4deb8297f7568cc8e376fdafff9e4282a9c83366..9c87eb0b340dba26b5b6fafca5d20d63b7b63951 100644 (file)
@@ -8,34 +8,36 @@
 """bridges the PropertyLoader (i.e. a relation()) and the UOWTransaction 
 together to allow processing of scalar- and list-based dependencies at flush time."""
 
+import sync
 from sync import ONETOMANY,MANYTOONE,MANYTOMANY
 from sqlalchemy import sql, util
 import session as sessionlib
 
-def create_dependency_processor(key, syncrules, cascade, secondary=None, association=None, is_backref=False, post_update=False):
+def create_dependency_processor(prop):
     types = {
         ONETOMANY : OneToManyDP,
         MANYTOONE: ManyToOneDP,
         MANYTOMANY : ManyToManyDP,
     }
-    if association is not None:
-        return AssociationDP(key, syncrules, cascade, secondary, association, is_backref, post_update)
+    if prop.association is not None:
+        return AssociationDP(prop)
     else:
-        return types[syncrules.direction](key, syncrules, cascade, secondary, association, is_backref, post_update)
+        return types[prop.direction](prop)
 
 class DependencyProcessor(object):
-    def __init__(self, key, syncrules, cascade, secondary=None, association=None, is_backref=False, post_update=False):
-        # TODO: update instance variable names to be more meaningful
-        self.syncrules = syncrules
-        self.cascade = cascade
-        self.mapper = syncrules.child_mapper
-        self.parent = syncrules.parent_mapper
-        self.association = association
-        self.secondary = secondary
-        self.direction = syncrules.direction
-        self.is_backref = is_backref
-        self.post_update = post_update
-        self.key = key
+    def __init__(self, prop):
+        self.prop = prop
+        self.cascade = prop.cascade
+        self.mapper = prop.mapper
+        self.parent = prop.parent
+        self.association = prop.association
+        self.secondary = prop.secondary
+        self.direction = prop.direction
+        self.is_backref = prop.is_backref
+        self.post_update = prop.post_update
+        self.key = prop.key
+
+        self._compile_synchronizers()
 
     def register_dependencies(self, uowcommit):
         """tells a UOWTransaction what mappers are dependent on which, with regards
@@ -75,6 +77,23 @@ class DependencyProcessor(object):
         """called during a flush to synchronize primary key identifier values between a parent/child object, as well as 
         to an associationrow in the case of many-to-many."""
         raise NotImplementedError()
+
+    def _compile_synchronizers(self):
+        """assembles a list of 'synchronization rules', which are instructions on how to populate
+        the objects on each side of a relationship.  This is done when a PropertyLoader is 
+        first initialized.
+
+        The list of rules is used within commits by the _synchronize() method when dependent 
+        objects are processed."""
+        parent_tables = util.Set(self.parent.tables + [self.parent.mapped_table])
+        target_tables = util.Set(self.mapper.tables + [self.mapper.mapped_table])
+
+        self.syncrules = sync.ClauseSynchronizer(self.parent, self.mapper, self.direction)
+        if self.direction == sync.MANYTOMANY:
+            self.syncrules.compile(self.prop.primaryjoin, parent_tables, [self.secondary], False)
+            self.syncrules.compile(self.prop.secondaryjoin, target_tables, [self.secondary], True)
+        else:
+            self.syncrules.compile(self.prop.primaryjoin, parent_tables, target_tables)
         
     def get_object_dependencies(self, obj, uowcommit, passive = True):
         """returns the list of objects that are dependent on the given object, as according to the relationship
@@ -82,6 +101,13 @@ class DependencyProcessor(object):
         return sessionlib.attribute_manager.get_history(obj, self.key, passive = passive)
 
     def _conditional_post_update(self, obj, uowcommit, related):
+        """execute a post_update call.
+        
+        for relations that contain the post_update flag, an additional UPDATE statement may be
+        associated after an INSERT or before a DELETE in order to resolve circular row dependencies.
+        This method will check for the post_update flag being set on a particular relationship, and
+        given a target object and list of one or more related objects, and execute the UPDATE if the
+        given related object list contains INSERTs or DELETEs."""
         if obj is not None and self.post_update:
             for x in related:
                 if x is not None and (uowcommit.is_deleted(x) or not hasattr(x, '_instance_key')):
index d535ccc76493e99d90a25042bf7fcc44333c81e8..f76f46036ade1d8ae477b38db945a274806bde05 100644 (file)
@@ -9,8 +9,8 @@ well as relationships.  also defines some MapperOptions that can be used with th
 properties."""
 
 from sqlalchemy import sql, schema, util, attributes, exceptions, sql_util, logging
-import sync
 import mapper
+import sync
 import session as sessionlib
 import dependency
 import util as mapperutil
@@ -250,7 +250,7 @@ class PropertyLoader(mapper.MapperProperty):
             raise exceptions.ArgumentError("Error determining primary and/or secondary join for relationship '%s' between mappers '%s' and '%s'.  If the underlying error cannot be corrected, you should specify the 'primaryjoin' (and 'secondaryjoin', if there is an association table present) keyword arguments to the relation() function (or for backrefs, by specifying the backref using the backref() function with keyword arguments) to explicitly specify the join conditions.  Nested error is \"%s\"" % (self.key, self.localparent, self.mapper, str(e)))
         # if the foreign key wasnt specified and theres no assocaition table, try to figure
         # out who is dependent on who. we dont need all the foreign keys represented in the join,
-        # just one of them.  
+        # just one of them.
         if not len(self.foreignkey) and self.secondaryjoin is None:
             # else we usually will have a one-to-many where the secondary depends on the primary
             # but its possible that its reversed
@@ -273,8 +273,7 @@ class PropertyLoader(mapper.MapperProperty):
                 self._dependency_processor = self.inherits._dependency_processor
 
         if not hasattr(self, '_dependency_processor'):
-            self._compile_synchronizers()
-            self._dependency_processor = dependency.create_dependency_processor(self.key, self.syncrules, self.cascade, secondary=self.secondary, association=self.association, is_backref=self.is_backref, post_update=self.post_update)
+            self._dependency_processor = dependency.create_dependency_processor(self)
 
         if self.inherits is not None and not hasattr(self.inherits, '_dependency_processor'):
             self.inherits._dependency_processor = self._dependency_processor
@@ -307,12 +306,15 @@ class PropertyLoader(mapper.MapperProperty):
     def _set_class_attribute(self, class_, key):
         """sets attribute behavior on our target class."""
         self._register_attribute(class_)
+
+    def _is_self_referential(self):
+        return self.parent.mapped_table is self.target or self.parent.select_table is self.target
         
     def _get_direction(self):
         """determines our 'direction', i.e. do we represent one to many, many to many, etc."""
         if self.secondaryjoin is not None:
             return sync.MANYTOMANY
-        elif self.parent.mapped_table is self.target or self.parent.select_table is self.target:
+        elif self._is_self_referential():
             if list(self.foreignkey)[0].primary_key:
                 return sync.MANYTOONE
             else:
@@ -356,22 +358,6 @@ class PropertyLoader(mapper.MapperProperty):
         if not self.viewonly:
             self._dependency_processor.register_dependencies(uowcommit)
             
-    def _compile_synchronizers(self):
-        """assembles a list of 'synchronization rules', which are instructions on how to populate
-        the objects on each side of a relationship.  This is done when a PropertyLoader is 
-        first initialized.
-        
-        The list of rules is used within commits by the _synchronize() method when dependent 
-        objects are processed."""
-        parent_tables = util.Set(self.parent.tables + [self.parent.mapped_table])
-        target_tables = util.Set(self.mapper.tables + [self.mapper.mapped_table])
-
-        self.syncrules = sync.ClauseSynchronizer(self.parent, self.mapper, self.direction)
-        if self.direction == sync.MANYTOMANY:
-            self.syncrules.compile(self.primaryjoin, parent_tables, [self.secondary], False)
-            self.syncrules.compile(self.secondaryjoin, target_tables, [self.secondary], True)
-        else:
-            self.syncrules.compile(self.primaryjoin, parent_tables, target_tables)
 
 PropertyLoader.logger = logging.class_logger(PropertyLoader)
 
index d9ec5cde98bb83223b7881ac9c2d134a71a69a93..8c481b6f11645951ec113dd24800dd8df6e04781 100644 (file)
@@ -52,9 +52,14 @@ class QueueDependencySorter(object):
         def __str__(self):
             return self.safestr()
         def safestr(self, indent=0):
-            return (' ' * indent) + "%s  (idself=%s)" % (str(self.item), repr(id(self))) + repr(self.cycles) + "\n" + string.join([n.safestr(indent + 1) for n in self.children], '')
+            return (' ' * indent * 2) + \
+                str(self.item) + \
+                (self.cycles is not None and (" (cycles: " + repr([x for x in self.cycles]) + ")") or "") + \
+                "\n" + \
+                string.join([n.safestr(indent + 1) for n in self.children], '')
+                
         def describe(self):
-            return "%s  (idself=%s)" % (str(self.item), repr(id(self)))
+            return "%s" % (str(self.item))
         def __repr__(self):
             return self.describe()
         def is_dependent(self, child):
index 008236877c098facbbcaca4277ea517282c10944..68dfc3fbbb1ddabafa35ec274551e4105be48dd1 100644 (file)
@@ -377,9 +377,7 @@ class UOWTransaction(object):
             
         mappers = self._get_noninheriting_mappers()
         head = DependencySorter(self.dependencies, list(mappers)).sort(allow_all_cycles=True)
-        #print "-------------------------"
-        #print str(head)
-        #print "---------------------------"
+        self.logger.debug("Dependency sort:\n"+ str(head))
         task = sort_hier(head)
         return task
 
@@ -722,7 +720,7 @@ class UOWTask(object):
         
         #print "BEGIN CIRC SORT-------"
         #print "PRE-CIRC:"
-        #print list(cycles)[0].dump()
+        #print list(cycles) #[0].dump()
         
         # dependency processors that arent part of the cyclical thing
         # get put here