]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
working on [ticket:190], getting circular dependency sort to work with inheriting...
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 29 May 2006 05:17:21 +0000 (05:17 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 29 May 2006 05:17:21 +0000 (05:17 +0000)
lib/sqlalchemy/orm/dependency.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/unitofwork.py

index b122b621d4d25199c8affca476cec5996d9581ac..195aff2b08b11dbe11b28e0ee0fbadb9a300d747 100644 (file)
@@ -378,7 +378,7 @@ class MapperStub(object):
         pass
     def delete_obj(self, *args, **kwargs):
         pass
-    def _primary_mapper(self):
+    def primary_mapper(self):
         return self
     def base_mapper(self):
         return self
\ No newline at end of file
index 3907b0b12ebebdfe7bcee169d2fb47d1a6b11027..07e4f503d3c0f9d4ae30bd948f95e23bbde5fde0 100644 (file)
@@ -111,7 +111,7 @@ class Mapper(object):
         # tables - a collection of underlying Table objects pulled from mapped_table
         
         for table in (local_table, select_table):
-            if table is not None and isinstance(local_table, sql.SelectBaseMixin):
+            if table is not None and isinstance(table, sql.SelectBaseMixin):
                 # some db's, noteably postgres, dont want to select from a select
                 # without an alias.  also if we make our own alias internally, then
                 # the configured properties on the mapper are not matched against the alias 
@@ -174,6 +174,7 @@ class Mapper(object):
         else:
             self.select_table = self.mapped_table
         self.unjoined_table = self.local_table
+
             
         # locate all tables contained within the "table" passed in, which
         # may be a join or other construct
@@ -265,11 +266,6 @@ class Mapper(object):
             if isinstance(self.polymorphic_map[key], type):
                 self.polymorphic_map[key] = class_mapper(self.polymorphic_map[key])
 
-        l = [(key, prop) for key, prop in self.props.iteritems()]
-        for key, prop in l:
-            if getattr(prop, 'key', None) is None:
-                prop.init(key, self)
-
         # select_table specified...set up a surrogate mapper that will be used for selects
         # select_table has to encompass all the columns of the mapped_table either directly
         # or through proxying relationships
@@ -279,9 +275,15 @@ class Mapper(object):
                 for key, prop in properties.iteritems():
                     if sql.is_column(prop):
                         props[key] = self.select_table.corresponding_column(prop)
-                    elif (isinstance(column, list) and sql.is_column(column[0])):
+                    elif (isinstance(prop, list) and sql.is_column(prop[0])):
                         props[key] = [self.select_table.corresponding_column(c) for c in prop]
             self.__surrogate_mapper = Mapper(self.class_, self.select_table, non_primary=True, properties=props, polymorphic_map=self.polymorphic_map, polymorphic_on=self.polymorphic_on)
+
+        l = [(key, prop) for key, prop in self.props.iteritems()]
+        for key, prop in l:
+            if getattr(prop, 'key', None) is None:
+                prop.init(key, self)
+
     
     def base_mapper(self):
         """returns the ultimate base mapper in an inheritance chain"""
@@ -356,7 +358,7 @@ class Mapper(object):
         """returns True if this mapper is the primary mapper for its class key (class + entity_name)"""
         return mapper_registry.get(self.class_key, None) is self
 
-    def _primary_mapper(self):
+    def primary_mapper(self):
         """returns the primary mapper corresponding to this mapper's class key (class + entity_name)"""
         return mapper_registry[self.class_key]
 
@@ -514,8 +516,6 @@ class Mapper(object):
     def _setattrbycolumn(self, obj, column, value):
         self.columntoproperty[column][0].setattr(obj, value)
     
-    def primary_mapper(self):
-        return mapper_registry[self.class_key]
             
     def save_obj(self, objects, uow, postupdate=False):
         """called by a UnitOfWork object to save objects, which involves either an INSERT or
@@ -1103,6 +1103,8 @@ class TranslatingDict(dict):
         self.selectable = selectable
     def __translate_col(self, col):
         ourcol = self.selectable.corresponding_column(col, keys_ok=False, raiseerr=False)
+        #if col is not ourcol:
+        #    print "TD TRANSLATING ", col, "TO", ourcol
         if ourcol is None:
             return col
         else:
index 59b85c0578b3841e1e50bb4c2fc04cff285e1f9f..9f8883a3106a73be9223c2eaf255d000d98106c3 100644 (file)
@@ -174,7 +174,7 @@ class PropertyLoader(mapper.MapperProperty):
     def do_init_subclass(self, key, parent):
         """template method for subclasses of PropertyLoader"""
         pass
-        
+    
     def do_init(self, key, parent):
         import sqlalchemy.orm
         if isinstance(self.argument, type):
@@ -274,18 +274,18 @@ class PropertyLoader(mapper.MapperProperty):
                 return
             if binary.left.primary_key:
                 if dependent[0] is binary.left.table:
-                    raise exceptions.ArgumentError("Could not determine the parent/child relationship for property '%s', based on join condition '%s' (table '%s' appears on both sides of the relationship, or in an otherwise ambiguous manner). please specify the 'foreignkey' keyword parameter to the relation() function indicating a column on the remote side of the relationship" % (self.key, str(self.primaryjoin), str(binary.left.table)))
+                    raise exceptions.ArgumentError("Could not determine the parent/child relationship for property '%s', based on join condition '%s' (table '%s' appears on both sides of the relationship, or in an otherwise ambiguous manner). please specify the 'foreignkey' keyword parameter to the relation() function indicating a column on the remote side of the relationship" % (self.key, str(self.primaryjoin), binary.left.table.name))
                 dependent[0] = binary.right.table
                 self.foreignkey= binary.right
             elif binary.right.primary_key:
                 if dependent[0] is binary.right.table:
-                    raise exceptions.ArgumentError("Could not determine the parent/child relationship for property '%s', based on join condition '%s' (table '%s' appears on both sides of the relationship, or in an otherwise ambiguous manner). please specify the 'foreignkey' keyword parameter to the relation() function indicating a column on the remote side of the relationship" % (self.key, str(self.primaryjoin), str(binary.right.table)))
+                    raise exceptions.ArgumentError("Could not determine the parent/child relationship for property '%s', based on join condition '%s' (table '%s' appears on both sides of the relationship, or in an otherwise ambiguous manner). please specify the 'foreignkey' keyword parameter to the relation() function indicating a column on the remote side of the relationship" % (self.key, str(self.primaryjoin), binary.right.table.name))
                 dependent[0] = binary.left.table
                 self.foreignkey = binary.left
         visitor = BinaryVisitor(foo)
         self.primaryjoin.accept_visitor(visitor)
         if dependent[0] is None:
-            raise exceptions.ArgumentError("Could not determine the parent/child relationship for property '%s', based on join condition '%s' (no relationships joining tables '%s' and '%s' could be located). please specify the 'foreignkey' keyword parameter to the relation() function indicating a column on the remote side of the relationship" % (self.key, str(self.primaryjoin), str(binary.left.table), str(binary.right.table)))
+            raise exceptions.ArgumentError("Could not determine the parent/child relationship for property '%s', based on join condition '%s' (no relationships joining tables '%s' and '%s' could be located). please specify the 'foreignkey' keyword parameter to the relation() function indicating a column on the remote side of the relationship" % (self.key, str(self.primaryjoin), str(binary.left.table), binary.right.table.name))
         else:
             self.foreigntable = dependent[0]
 
@@ -637,7 +637,7 @@ class BackRef(object):
         PropertyLoader's mapper."""
         # try to set a LazyLoader on our mapper referencing the parent mapper
         mapper = prop.mapper.primary_mapper()
-        if not prop.mapper.props.has_key(self.key):
+        if not mapper.props.has_key(self.key):
             pj = self.kwargs.pop('primaryjoin', None)
             sj = self.kwargs.pop('secondaryjoin', None)
             # TODO: we are going to have the newly backref'd property create its 
index e6d5c424e133eff6f308bbd961c7115791be4f42..1ebf9cd6981c132ac7b65b96d108b0de37c26fb3 100644 (file)
@@ -336,8 +336,8 @@ class UOWTransaction(object):
         """called by mapper.PropertyLoader to register the objects handled by
         one mapper being dependent on the objects handled by another."""
         # correct for primary mapper (the mapper offcially associated with the class)
-        mapper = mapper._primary_mapper().base_mapper()
-        dependency = dependency._primary_mapper().base_mapper()
+        mapper = mapper.primary_mapper().base_mapper()
+        dependency = dependency.primary_mapper().base_mapper()
         
         self.dependencies[(mapper, dependency)] = True
         self._mark_modified()
@@ -353,8 +353,8 @@ class UOWTransaction(object):
         #print "registerprocessor", str(mapper), repr(processor.key), str(mapperfrom)
         
         # correct for primary mapper (the mapper offcially associated with the class)
-        mapper = mapper._primary_mapper()
-        mapperfrom = mapperfrom._primary_mapper()
+        mapper = mapper.primary_mapper()
+        mapperfrom = mapperfrom.primary_mapper()
         
         task = self.get_task_by_mapper(mapper)
         targettask = self.get_task_by_mapper(mapperfrom)
@@ -678,6 +678,12 @@ class UOWTask(object):
         self._execute_per_element_childtasks(trans, True)
         self._delete_objects(trans)
 
+    def polymorphic_tasks(self):
+        yield self
+        for task in self.inheriting_tasks:
+            for t in task.polymorphic_tasks():
+                yield t
+                
     def contains_object(self, obj, polymorphic=False):
         if obj in self.objects:
             return True
@@ -745,55 +751,59 @@ class UOWTask(object):
             return l
 
         def dependency_in_cycles(dep):
-            proctask = trans.get_task_by_mapper(dep.processor.mapper, True)
+            proctask = trans.get_task_by_mapper(dep.processor.mapper.primary_mapper(), True)
             return dep.targettask in cycles and (proctask is not None and proctask in cycles)
             
         # organize all original UOWDependencyProcessors by their target task
         deps_by_targettask = {}
-        for task in cycles:
-            for dep in task.dependencies:
-                if not dependency_in_cycles(dep):
-                    extradeplist.append(dep)
-                l = deps_by_targettask.setdefault(dep.targettask, [])
-                l.append(dep)
-
-        for task in cycles:
-            for taskelement in task.get_elements(polymorphic=True):
-                obj = taskelement.obj
-                object_to_original_task[obj] = task
-                #print "OBJ", repr(obj), "TASK", repr(task)
-                
-                for dep in deps_by_targettask.get(task, []):
-                    # is this dependency involved in one of the cycles ?
+        for t in cycles:
+            for task in t.polymorphic_tasks():
+                for dep in task.dependencies:
                     if not dependency_in_cycles(dep):
-                        continue
-                        
-                    (processor, targettask) = (dep.processor, dep.targettask)
-                    isdelete = taskelement.isdelete
+                        extradeplist.append(dep)
+                    l = deps_by_targettask.setdefault(dep.targettask, [])
+                    l.append(dep)
+
+        for t in cycles:
+            for task in t.polymorphic_tasks():
+                for taskelement in task.get_elements(polymorphic=False):
+                    obj = taskelement.obj
+                    object_to_original_task[obj] = task
+                    #print "OBJ", repr(obj), "TASK", repr(task)
+                
+                    for dep in deps_by_targettask.get(task, []):
+                        # is this dependency involved in one of the cycles ?
+                        if not dependency_in_cycles(dep):
+                            continue
+                        #print "DEP", dep.processor.key    
+                        (processor, targettask) = (dep.processor, dep.targettask)
+                        isdelete = taskelement.isdelete
                     
-                    # list of dependent objects from this object
-                    childlist = dep.get_object_dependencies(obj, trans, passive = True)
+                        # list of dependent objects from this object
+                        childlist = dep.get_object_dependencies(obj, trans, passive = True)
                     
-                    # the task corresponding to saving/deleting of those dependent objects
-                    childtask = trans.get_task_by_mapper(processor.mapper)
+                        # the task corresponding to saving/deleting of those dependent objects
+                        childtask = trans.get_task_by_mapper(processor.mapper.primary_mapper())
                     
-                    childlist = childlist.added_items() + childlist.unchanged_items() + childlist.deleted_items()
+                        childlist = childlist.added_items() + childlist.unchanged_items() + childlist.deleted_items()
                         
-                    for o in childlist:
-                        if o is None or not childtask.contains_object(o, polymorphic=True):
-                            continue
-                        whosdep = dep.whose_dependent_on_who(obj, o)
-                        if whosdep is not None:
-                            tuples.append(whosdep)
-                            # create a UOWDependencyProcessor representing this pair of objects.
-                            # append it to a UOWTask
-                            if whosdep[0] is obj:
-                                get_dependency_task(whosdep[0], dep).append(whosdep[0], isdelete=isdelete)
+                        for o in childlist:
+                            if o is None or not childtask.contains_object(o, polymorphic=True):
+                                continue
+                            #print "CHILD", o
+                            whosdep = dep.whose_dependent_on_who(obj, o)
+                            if whosdep is not None:
+                                tuples.append(whosdep)
+                                # create a UOWDependencyProcessor representing this pair of objects.
+                                # append it to a UOWTask
+                                if whosdep[0] is obj:
+                                    get_dependency_task(whosdep[0], dep).append(whosdep[0], isdelete=isdelete)
+                                else:
+                                    get_dependency_task(whosdep[0], dep).append(whosdep[1], isdelete=isdelete)
                             else:
-                                get_dependency_task(whosdep[0], dep).append(whosdep[1], isdelete=isdelete)
-                        else:
-                            get_dependency_task(obj, dep).append(obj, isdelete=isdelete)
-                        
+                                get_dependency_task(obj, dep).append(obj, isdelete=isdelete)
+        
+        #print "TUPLES", tuples
         head = DependencySorter(tuples, allobjects).sort()
         if head is None:
             return None
@@ -818,6 +828,7 @@ class UOWTask(object):
                     
             can_add_to_parent = t.mapper is parenttask.mapper
             original_task = object_to_original_task[node.item]
+            #print "ORIG TASK", original_task
             if original_task.contains_object(node.item, polymorphic=False):
                 if can_add_to_parent:
                     parenttask.append(node.item, original_task.objects[node.item].listonly, isdelete=original_task.objects[node.item].isdelete, childtask=t)