[SQL-CVS] r2355 - SQLObject/trunk/sqlobject/manager
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
|
From: <sub...@co...> - 2007-02-28 15:27:25
|
Author: phd
Date: 2007-02-28 08:27:23 -0700 (Wed, 28 Feb 2007)
New Revision: 2355
Modified:
SQLObject/trunk/sqlobject/manager/command.py
Log:
Create/drop the tables in the order of dependency level.
Modified: SQLObject/trunk/sqlobject/manager/command.py
===================================================================
--- SQLObject/trunk/sqlobject/manager/command.py 2007-02-28 15:27:04 UTC (rev 2354)
+++ SQLObject/trunk/sqlobject/manager/command.py 2007-02-28 15:27:23 UTC (rev 2355)
@@ -16,6 +16,7 @@
from sqlobject import col
from sqlobject.util import moduleloader
from sqlobject.declarative import DeclarativeMeta
+from sqlobject.classregistry import findClass
# It's not very unsafe to use tempnam like we are doing:
warnings.filterwarnings(
@@ -181,6 +182,81 @@
help = ''
+ def orderClassesByDependencyLevel(self, classes):
+ """
+ Return classes ordered by their depth in the class dependency
+ tree (this is *not* the inheritance tree), from the
+ top level (independant) classes to the deepest level.
+ The dependency tree is defined by the foreign key relations.
+ """
+ # @@: written as a self-contained function for now, to prevent
+ # having to modify any core SQLObject component and namespace
+ # contamination.
+ # yemartin - 2006-08-08
+
+ class SQLObjectCircularReferenceError(Exception): pass
+
+ def findReverseDependencies(cls):
+ """
+ Return a list of classes that cls depends on. Note that
+ "depends on" here mean "has a foreign key pointing to".
+ """
+ depended = []
+ for col in cls.sqlmeta.columnList:
+ if col.foreignKey:
+ other = findClass(col.foreignKey,
+ col.soClass.sqlmeta.registry)
+ if other not in depended:
+ depended.append(other)
+ return depended
+
+ # Cache to save already calculated dependency levels.
+ dependency_levels = {}
+ def calculateDependencyLevel(cls, dependency_stack=[]):
+ """
+ Recursively calculate the dependency level of cls, while
+ using the dependency_stack to detect any circular reference.
+ """
+ # Return value from the cache if already calculated
+ if dependency_levels.has_key(cls):
+ return dependency_levels[cls]
+ # Check for circular references
+ if cls in dependency_stack:
+ dependency_stack.append(cls)
+ raise SQLObjectCircularReferenceError, (
+ "Found a circular reference: %s " %
+ (' --> '.join([x.__name__
+ for x in dependency_stack])))
+ dependency_stack.append(cls)
+ # Recursively inspect dependent classes.
+ depended = findReverseDependencies(cls)
+ if depended:
+ level = max([calculateDependencyLevel(x, dependency_stack)
+ for x in depended]) + 1
+ else:
+ level = 0
+ dependency_levels[cls] = level
+ return level
+
+ # Now simply calculate and sort by dependency levels:
+ try:
+ sorter = []
+ for cls in classes:
+ level = calculateDependencyLevel(cls)
+ sorter.append((level, cls))
+ sorter.sort()
+ ordered_classes = [cls for level, cls in sorter]
+ except SQLObjectCircularReferenceError, msg:
+ # Failsafe: return the classes as-is if a circular reference
+ # prevented the dependency levels to be calculated.
+ print ("Warning: a circular reference was detected in the "
+ "model. Unable to sort the classes by dependency: they "
+ "will be treated in alphabetic order. This may or may "
+ "not work depending on your database backend. "
+ "The error was:\n%s" % msg)
+ return classes
+ return ordered_classes
+
def __classinit__(cls, new_args):
if cls.__bases__ == (object,):
# This abstract base class
@@ -285,7 +361,7 @@
else:
print 'No eggs specified'
sys.exit(1)
- return all
+ return self.orderClassesByDependencyLevel(all)
def classes_from_module(self, module):
all = []
@@ -613,7 +689,7 @@
v = self.options.verbose
dropped = 0
not_existing = 0
- for soClass in self.classes():
+ for soClass in self.classes().__reversed__():
exists = soClass._connection.tableExists(soClass.sqlmeta.table)
if v >= 1:
if exists:
|