Thread: [SQL-CVS] SQLObject/SQLObject Col.py,1.33,1.34 DBConnection.py,1.56,1.57 SQLObject.py,1.64,1.65
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
From: <dre...@us...> - 2003-12-04 16:44:08
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject In directory sc8-pr-cvs1:/tmp/cvs-serv16262/SQLObject Modified Files: Col.py DBConnection.py SQLObject.py Log Message: Merge cascadegeddon-branch. This implements foreign key constraints (postgresql-only) and 'DELETE CASCADE'/'DELETE RESTRICT' (should work on all db's as it's done manually). We don't support 'UPDATE CASCADE'/'UPDATE RESTRICT' yet. To get it working, just pass the 'cascade' keyword argument to ForeignKey. True=CASCADE, False=RESTRICT, None(default)=The old behavior. Index: Col.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v retrieving revision 1.33 retrieving revision 1.34 diff -C2 -d -r1.33 -r1.34 *** Col.py 4 Dec 2003 16:36:16 -0000 1.33 --- Col.py 4 Dec 2003 16:44:04 -0000 1.34 *************** *** 34,38 **** columnDef=None, validator=None, ! immutable=False): # This isn't strictly true, since we *could* use backquotes or --- 34,39 ---- columnDef=None, validator=None, ! immutable=False, ! cascade=None): # This isn't strictly true, since we *could* use backquotes or *************** *** 50,53 **** --- 51,60 ---- self.immutable = immutable + # cascade can be one of: + # None: no constraint is generated + # True: a CASCADE constraint is generated + # False: a RESTRICT constraint is generated + self.cascade = cascade + if type(constraints) not in (type([]), type(())): constraints = [constraints] *************** *** 348,351 **** --- 355,377 ---- kw['name'] = style.instanceAttrToIDAttr(kw['name']) SOKeyCol.__init__(self, **kw) + + def postgresCreateSQL(self): + from SQLObject import findClass + sql = SOKeyCol.postgresCreateSQL(self) + if self.cascade is not None: + other = findClass(self.foreignKey) + tName = other._table + idName = other._idName + action = self.cascade and 'CASCADE' or 'RESTRICT' + constraint = ('CONSTRAINT %(tName)s_exists ' + 'FOREIGN KEY(%(colName)s) ' + 'REFERENCES %(tName)s(%(idName)s) ' + 'ON DELETE %(action)s' % + {'tName':tName, + 'colName':self.dbName, + 'idName':idName, + 'action':action}) + sql = ', '.join([sql, constraint]) + return sql class ForeignKey(KeyCol): Index: DBConnection.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v retrieving revision 1.56 retrieving revision 1.57 diff -C2 -d -r1.56 -r1.57 *** DBConnection.py 4 Dec 2003 16:36:16 -0000 1.56 --- DBConnection.py 4 Dec 2003 16:44:04 -0000 1.57 *************** *** 280,284 **** assert 0, "Implement in subclasses" ! def dropTable(self, tableName): self.query("DROP TABLE %s" % tableName) --- 280,284 ---- assert 0, "Implement in subclasses" ! def dropTable(self, tableName, cascade=False): self.query("DROP TABLE %s" % tableName) *************** *** 614,617 **** --- 614,621 ---- return '%s SERIAL PRIMARY KEY' % soClass._idName + def dropTable(self, tableName, cascade=False): + self.query("DROP TABLE %s %s" % (tableName, + cascade and 'CASCADE' or '')) + def joinSQLType(self, join): return 'INT NOT NULL' *************** *** 982,986 **** column.firebirdCreateSQL())) ! def dropTable(self, tableName): self.query("DROP TABLE %s" % tableName) self.query("DROP GENERATOR GEN_%s" % tableName) --- 986,990 ---- column.firebirdCreateSQL())) ! def dropTable(self, tableName, cascade=False): self.query("DROP TABLE %s" % tableName) self.query("DROP GENERATOR GEN_%s" % tableName) *************** *** 1218,1222 **** self._meta["%s.id" % soClass._table] = "1" ! def dropTable(self, tableName): try: del self._meta["%s.id" % tableName] --- 1222,1226 ---- self._meta["%s.id" % soClass._table] = "1" ! def dropTable(self, tableName, cascade=False): try: del self._meta["%s.id" % tableName] Index: SQLObject.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v retrieving revision 1.64 retrieving revision 1.65 diff -C2 -d -r1.64 -r1.65 *** SQLObject.py 12 Nov 2003 17:06:00 -0000 1.64 --- SQLObject.py 4 Dec 2003 16:44:04 -0000 1.65 *************** *** 37,40 **** --- 37,41 ---- class SQLObjectNotFound(LookupError): pass + class SQLObjectIntegrityError(Exception): pass True, False = 1==1, 0==1 *************** *** 312,315 **** --- 313,329 ---- return classRegistry[registry][name] + def findDependencies(name, registry=None): + depends = [] + for n, klass in classRegistry[registry].items(): + if findDependantColumns(name, klass): + depends.append(klass) + return depends + + def findDependantColumns(name, klass): + depends = [] + for col in klass._SO_columns: + if col.foreignKey == name and col.cascade is not None: + depends.append(col) + return depends class CreateNewSQLObject: *************** *** 933,936 **** --- 947,954 ---- _SO_fetchAlternateID = classmethod(_SO_fetchAlternateID) + def _SO_depends(cls): + return findDependencies(cls.__name__, cls._registry) + _SO_depends = classmethod(_SO_depends) + def select(cls, clause=None, clauseTables=None, orderBy=NoDefault, limit=None, *************** *** 952,959 **** # 3-03 @@: Should these have a connection argument? ! def dropTable(cls, ifExists=False, dropJoinTables=True): if ifExists and not cls._connection.tableExists(cls._table): return ! cls._connection.dropTable(cls._table) if dropJoinTables: cls.dropJoinTables(ifExists=ifExists) --- 970,977 ---- # 3-03 @@: Should these have a connection argument? ! def dropTable(cls, ifExists=False, dropJoinTables=True, cascade=False): if ifExists and not cls._connection.tableExists(cls._table): return ! cls._connection.dropTable(cls._table, cascade) if dropJoinTables: cls.dropJoinTables(ifExists=ifExists) *************** *** 1010,1013 **** --- 1028,1054 ---- def destroySelf(self): # Kills this object. Kills it dead! + depends = [] + klass = self.__class__ + depends = self._SO_depends() + for k in depends: + cols = findDependantColumns(klass.__name__, k) + query = [] + restrict = False + for col in cols: + if col.cascade == False: + # Found a restriction + restrict = True + query.append("%s = %s" % (col.dbName, self.id)) + query = ' OR '.join(query) + results = k.select(query) + if restrict and results.count(): + # Restrictions only apply if there are + # matching records on the related table + raise SQLObjectIntegrityError, ( + "Tried to delete %s::%s but " + "table %s has a restriction against it" % + (klass.__name__, self.id, k.__name__)) + for row in results: + row.destroySelf() self._SO_obsolete = True self._connection._SO_delete(self) |