Ticket #279: command.patch

File command.patch, 4.5 kB (added by yemartin, 2 years ago)

A tentative patch that should fix both #279 and #882. (Update: fixed date in comment, I was 2 years early ;)

  • sqlobject\manager\command.py

    old new  
    1818from sqlobject import col 
    1919from sqlobject.util import moduleloader 
    2020from sqlobject.declarative import DeclarativeMeta 
     21from sqlobject.classregistry import findClass 
    2122 
    2223# It's not very unsafe to use tempnam like we are doing: 
    2324warnings.filterwarnings( 
     
    183184 
    184185    help = '' 
    185186 
     187    def orderClassesByDependencyLevel(self, classes): 
     188        """ 
     189        Return classes ordered by their depth in the class dependency 
     190        tree (this is *not* the inheritance tree), from the 
     191        top level (independant) classes to the deepest level. 
     192        The dependency tree is defined by the foreign key relations. 
     193        """ 
     194        # @@: written as a self-contained function for now, to prevent 
     195        # having to modify any core SQLObject component and namespace 
     196        # contamination. 
     197        # yemartin - 2006-08-08 
     198         
     199        class SQLObjectCircularReferenceError(Exception): pass 
     200 
     201        def findReverseDependencies(cls): 
     202            """ 
     203            Return a list of classes that cls depends on. Note that 
     204            "depends on" here mean "has a foreign key pointing to". 
     205            """ 
     206            depended = [] 
     207            for col in cls.sqlmeta.columnList: 
     208                if col.foreignKey: 
     209                    other = findClass(col.foreignKey, 
     210                                      col.soClass.sqlmeta.registry) 
     211                    if other not in depended: 
     212                        depended.append(other) 
     213            return depended 
     214         
     215        # Cache to save already calculated dependency levels. 
     216        dependency_levels = {} 
     217        def calculateDependencyLevel(cls, dependency_stack=[]): 
     218            """ 
     219            Recursively calculate the dependency level of cls, while 
     220            using the dependency_stack to detect any circular reference. 
     221            """ 
     222            # Return value from the cache if already calculated 
     223            if dependency_levels.has_key(cls): 
     224                return dependency_levels[cls] 
     225            # Check for circular references 
     226            if cls in dependency_stack: 
     227                dependency_stack.append(cls) 
     228                raise SQLObjectCircularReferenceError, ( 
     229                        "Found a circular reference: %s " % 
     230                        (' --> '.join([x.__name__ 
     231                                       for x in dependency_stack]))) 
     232            dependency_stack.append(cls) 
     233            # Recursively inspect dependent classes. 
     234            depended = findReverseDependencies(cls) 
     235            if depended: 
     236                level = max([calculateDependencyLevel(x, dependency_stack) 
     237                             for x in depended]) + 1 
     238            else: 
     239                level = 0 
     240            dependency_levels[cls] = level 
     241            return level 
     242         
     243        # Now simply calculate and sort by dependency levels: 
     244        try: 
     245            sorter = [] 
     246            for cls in classes: 
     247                level = calculateDependencyLevel(cls) 
     248                sorter.append((level, cls)) 
     249            sorter.sort() 
     250            ordered_classes = [cls for level, cls in sorter] 
     251        except SQLObjectCircularReferenceError, msg: 
     252            # Failsafe: return the classes as-is if a circular reference 
     253            # prevented the dependency levels to be calculated. 
     254            print ("Warning: a circular reference was detected in the " 
     255                    "model. Unable to sort the classes by dependency: they " 
     256                    "will be treated in alphabetic order. This may or may " 
     257                    "not work depending on your database backend. " 
     258                    "The error was:\n%s" % msg) 
     259            return classes 
     260        return ordered_classes 
     261 
    186262    def __classinit__(cls, new_args): 
    187263        if cls.__bases__ == (object,): 
    188264            # This abstract base class 
     
    287363            else: 
    288364                print 'No eggs specified' 
    289365            sys.exit(1) 
    290         return all 
     366        return self.orderClassesByDependencyLevel(all) 
    291367 
    292368    def classes_from_module(self, module): 
    293369        all = [] 
     
    583659        v = self.options.verbose 
    584660        dropped = 0 
    585661        not_existing = 0 
    586         for soClass in self.classes()
     662        for soClass in self.classes().__reversed__()
    587663            exists = soClass._connection.tableExists(soClass.sqlmeta.table) 
    588664            if v >= 1: 
    589665                if exists: