Hi!

I've patched SQLObject to do late reference on creation of tables.

It is no 100%, but it is a starting point.

I've attached the patch.

--
JP

On 10/1/05, Ian Bicking <[EMAIL PROTECTED]> wrote:

João Paulo Fernandes Farias wrote:
> Hi!
>
> How do I create tables with cyclic references?
>
> As an example, suppose we have two classes:
>
> class A(SQLObject):
>     b = ForeignKey('B')
>
> class B(SQLObject):
>     a = ForeignKey('A')
>
>
> /me using turbogears...
>
> I issue a: turbogears-admin.py sql create
>
> It gives me some erros because it tries to create table A but B does not
> exists, so it cannot be referenced.
>
> My suggestion is: Create all tables and then issue all the "alter table
> add constraint" as necessary.

This is kind of hard.  Technically you can do it like:

class A(SQLObject):
    b = ForeignKey('B')

class B(SQLObject):
    pass

def create():
    B.createTable()
    A.createTable()
    B.sqlmeta.addColumn(ForeignKey(name='a', foreignKey='A'),
changeSchema=True)


However, this won't work with sqlobject-admin (and thus
turbogears-admin.py).

Index: sqlobject/dbconnection.py
===================================================================
--- sqlobject/dbconnection.py	(revisão 1061)
+++ sqlobject/dbconnection.py	(cópia de trabalho)
@@ -513,11 +513,25 @@
         assert 0, 'Implement in subclasses'
 
     def createTable(self, soClass):
-        self.query(self.createTableSQL(soClass))
+        createSql, constraints = self.createTableSQL(soClass)
+        self.query(createSql)
 
+        return constraints
+
+    def createReferenceConstraints(self, soClass):
+        refConstraints = [self.createReferenceConstraint(soClass, column) \
+                          for column in soClass.sqlmeta.columnList \
+                          if isinstance(column, col.SOForeignKey)]
+        refConstraintDefs = [constraint \
+                             for constraint in refConstraints \
+                             if constraint]
+        return refConstraintDefs
+
     def createTableSQL(self, soClass):
-        return ('CREATE TABLE %s (\n%s\n)' %
+        constraints = self.createReferenceConstraints(soClass)
+        createSql = ('CREATE TABLE %s (\n%s\n)' %
                 (soClass.sqlmeta.table, self.createColumns(soClass)))
+        return createSql, constraints
 
     def createColumns(self, soClass):
         columnDefs = [self.createIDColumn(soClass)] \
@@ -525,6 +539,9 @@
                         for col in soClass.sqlmeta.columnList]
         return ",\n".join(["    %s" % c for c in columnDefs])
 
+    def createReferenceConstraint(self, soClass, col):
+        assert 0, "Implement in subclasses"
+
     def createColumn(self, soClass, col):
         assert 0, "Implement in subclasses"
 
Index: sqlobject/col.py
===================================================================
--- sqlobject/col.py	(revisão 1061)
+++ sqlobject/col.py	(cópia de trabalho)
@@ -707,6 +707,10 @@
 
     def postgresCreateSQL(self):
         sql = SOKeyCol.postgresCreateSQL(self)
+        return sql
+
+    def postgresCreateReferenceConstraint(self):
+        sTName = self.soClass.sqlmeta.table
         other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
         tName = other.sqlmeta.table
         idName = other.sqlmeta.idName
@@ -719,16 +723,16 @@
                 action = 'ON DELETE RESTRICT'
         else:
             action = ''
-        constraint = ('CONSTRAINT %(colName)s_exists '
+        constraint = ('ALTER TABLE %(sTName)s ADD CONSTRAINT %(colName)s_exists '
                       'FOREIGN KEY (%(colName)s) '
                       'REFERENCES %(tName)s (%(idName)s) '
                       '%(action)s' %
                       {'tName': tName,
                        'colName': self.dbName,
                        'idName': idName,
-                       'action': action})
-        sql = ', '.join([sql, constraint])
-        return sql
+                       'action': action,
+                       'sTName': sTName})
+        return constraint
 
     def sybaseCreateSQL(self):
         sql = SOKeyCol.sybaseCreateSQL(self)
Index: sqlobject/postgres/pgconnection.py
===================================================================
--- sqlobject/postgres/pgconnection.py	(revisão 1061)
+++ sqlobject/postgres/pgconnection.py	(cópia de trabalho)
@@ -138,6 +138,9 @@
     def createColumn(self, soClass, col):
         return col.postgresCreateSQL()
 
+    def createReferenceConstraint(self, soClass, col):
+        return col.postgresCreateReferenceConstraint()
+
     def createIndexSQL(self, soClass, index):
         return index.postgresCreateIndexSQL(soClass)
 
Index: sqlobject/manager/command.py
===================================================================
--- sqlobject/manager/command.py	(revisão 1061)
+++ sqlobject/manager/command.py	(cópia de trabalho)
@@ -502,11 +502,20 @@
 
     def command(self):
         classes = self.classes()
+        allConstraints = []
         for cls in classes:
             if self.options.verbose >= 1:
                 print '-- %s from %s' % (
                     cls.__name__, cls.__module__)
-            print cls.createTableSQL().strip() + ';\n'
+            createSql, constraints = cls.createTableSQL()
+            print createSql.strip() + ';\n'
+            allConstraints.append(constraints)
+        for constraints in allConstraints:
+            if constraints:
+                for constraint in constraints:
+                    if constraint:
+                        print constraint.strip() + ';\n'
+        
 
 class CommandList(Command):
 
@@ -540,6 +549,7 @@
         created = 0
         existing = 0
         dbs_created = []
+        constraints = {}
         for soClass in self.classes(require_some=True):
             if (self.options.create_db
                 and soClass._connection not in dbs_created):
@@ -548,6 +558,8 @@
                 else:
                     print '(simulating; cannot create database)'
                 dbs_created.append(soClass._connection)
+            if soClass._connection not in constraints.keys():
+                constraints[soClass._connection] = []
             exists = soClass._connection.tableExists(soClass.sqlmeta.table)
             if v >= 1:
                 if exists:
@@ -562,12 +574,22 @@
                 if self.options.interactive:
                     if self.ask('Create %s' % soClass.__name__):
                         created += 1
-                        soClass.createTable()
+                        tableConstraints = soClass.createTable()
+                        if tableConstraints:
+                            constraints[soClass._connection].append(tableConstraints)
+                          
                     else:
                         print 'Cancelled'
                 else:
                     created += 1
-                    soClass.createTable()
+                    tableConstraints = soClass.createTable()
+                    if tableConstraints:
+                        constraints[soClass._connection].append(tableConstraints)
+        for connection in constraints.keys():
+            for constraintList in constraints[connection]:
+                for constraint in constraintList:
+                    if constraint:
+                        connection.query(constraint)
         if v >= 1:
             print '%i tables created (%i already exist)' % (
                 created, existing)
Index: sqlobject/main.py
===================================================================
--- sqlobject/main.py	(revisão 1061)
+++ sqlobject/main.py	(cópia de trabalho)
@@ -1305,24 +1305,25 @@
         conn = connection or cls._connection
         if ifNotExists and conn.tableExists(cls.sqlmeta.table):
             return
-        conn.createTable(cls)
+        constraints = conn.createTable(cls)
         if createJoinTables:
             cls.createJoinTables(ifNotExists=ifNotExists,
                                  connection=conn)
         if createIndexes:
             cls.createIndexes(ifNotExists=ifNotExists,
                               connection=conn)
+        return constraints
     createTable = classmethod(createTable)
 
     def createTableSQL(cls, createJoinTables=True, connection=None,
                        createIndexes=True):
         conn = connection or cls._connection
-        sql = conn.createTableSQL(cls)
+        sql, constraints = conn.createTableSQL(cls)
         if createJoinTables:
             sql += '\n' + cls.createJoinTablesSQL(connection=conn)
         if createIndexes:
             sql += '\n' + cls.createIndexesSQL(connection=conn)
-        return sql
+        return sql, constraints
     createTableSQL = classmethod(createTableSQL)
 
     def createJoinTables(cls, ifNotExists=False, connection=None):

Reply via email to