Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv9293/SQLObject
Modified Files:
Col.py DBConnection.py SQLObject.py
Log Message:
* Col objects are more like definitions/factories, SO* classes
do the actual work (to the degree there is work there)
* SQLObject classes can inherit from each other
* New ._SO_columns attribute that holds the non-factory columns
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.18
retrieving revision 1.19
diff -C2 -d -r1.18 -r1.19
*** Col.py 5 May 2003 17:37:19 -0000 1.18
--- Col.py 6 May 2003 22:43:34 -0000 1.19
***************
*** 16,23 ****
# Col is essentially a column definition, it doesn't have
# much logic to it.
! class Col(object):
def __init__(self,
! name=None,
dbName=None,
default=NoDefault,
--- 16,24 ----
# Col is essentially a column definition, it doesn't have
# much logic to it.
! class SOCol(object):
def __init__(self,
! name,
! soClass,
dbName=None,
default=NoDefault,
***************
*** 26,32 ****
alternateMethodName=None,
constraints=None,
! notNull=False,
unique=NoDefault,
! sqlType=None):
# This isn't strictly true, since we *could* use backquotes
# around column names, but why would anyone *want* to
--- 27,36 ----
alternateMethodName=None,
constraints=None,
! notNull=NoDefault,
! notNone=NoDefault,
unique=NoDefault,
! sqlType=None,
! columnDef=None):
!
# This isn't strictly true, since we *could* use backquotes
# around column names, but why would anyone *want* to
***************
*** 37,55 ****
% repr(name)
assert name != 'id', 'The column name "id" is reserved for SQLObject use (and is implicitly created).'
! self.foreignKey = foreignKey
! self.alternateID = alternateID
! self.alternateMethodName = alternateMethodName
! if unique is NoDefault:
! self.unique = alternateID
! else:
! self.unique = unique
! self.constraints = constraints or []
if type(constraints) not in (type([]), type(())):
constraints = [constraints]
self.constraints = self.autoConstraints() + constraints
! self.notNull = notNull
! self.dbName = dbName
! if notNull:
self.constraints = [Constraints.notNull] + self.constraints
self.name = name
self.soClass = None
--- 41,63 ----
% repr(name)
assert name != 'id', 'The column name "id" is reserved for SQLObject use (and is implicitly created).'
! assert name, "You must provide a name for all columns"
!
! self.columnDef = columnDef
!
if type(constraints) not in (type([]), type(())):
constraints = [constraints]
self.constraints = self.autoConstraints() + constraints
!
! self.notNone = False
! if notNull is not NoDefault:
! self.notNone = notNull
! assert notNone is NoDefault or \
! (not notNone) == (not notNull), \
! "The notNull and notNone arguments are aliases, and must not conflict. You gave notNull=%r, notNone=%r" % (notNull, notNone)
! elif notNone is not NoDefault:
! self.notNone = notNone
! if self.notNone:
self.constraints = [Constraints.notNull] + self.constraints
+
self.name = name
self.soClass = None
***************
*** 57,76 ****
self.customSQLType = sqlType
- def setClass(self, soClass):
- if soClass is self.soClass:
- return
- assert not self.soClass, "This column (%r) has already been associated with another class (%r), and you tried to reassociate it with %r" % (self, self.soClass, soClass)
-
# if they don't give us a specific database name for
# the column, we separate the mixedCase into mixed_case
# and assume that. @@: should be able to define
# different policies for naming.
! if self.dbName is None:
self.dbName = soClass._style.pythonAttrToDBColumn(self.name)
# alternateID means that this is a unique column that
# can be used to identify rows
! if self.alternateID and self.alternateMethodName is None:
self.alternateMethodName = 'by' + self.name[0].capitalize() + self.name[1:]
if self.foreignKey:
assert self.name.upper().endswith('ID'), "All foreign key columns must end with 'ID' (%s)" % repr(self.name)
--- 65,91 ----
self.customSQLType = sqlType
# if they don't give us a specific database name for
# the column, we separate the mixedCase into mixed_case
# and assume that. @@: should be able to define
# different policies for naming.
! if dbName is None:
self.dbName = soClass._style.pythonAttrToDBColumn(self.name)
+ else:
+ self.dbName = dbName
+
# alternateID means that this is a unique column that
# can be used to identify rows
! self.alternateID = alternateID
! if self.alternateID and alternateMethodName is None:
self.alternateMethodName = 'by' + self.name[0].capitalize() + self.name[1:]
+ else:
+ self.alternateMethodName = alternateMethodName
+
+ if unique is NoDefault:
+ self.unique = alternateID
+ else:
+ self.unique = unique
+ self.foreignKey = foreignKey
if self.foreignKey:
assert self.name.upper().endswith('ID'), "All foreign key columns must end with 'ID' (%s)" % repr(self.name)
***************
*** 108,112 ****
if self.alternateID:
r += ' alternate ID'
! if self.notNull:
r += ' not null'
return r + '>'
--- 123,127 ----
if self.alternateID:
r += ' alternate ID'
! if self.notNone:
r += ' not null'
return r + '>'
***************
*** 117,121 ****
def _extraSQL(self):
result = []
! if self.notNull or self.alternateID:
result.append('NOT NULL')
if self.unique or self.alternateID:
--- 132,136 ----
def _extraSQL(self):
result = []
! if self.notNone or self.alternateID:
result.append('NOT NULL')
if self.unique or self.alternateID:
***************
*** 152,160 ****
return ' '.join([self.dbName, self._sqliteType()] + self._extraSQL())
! class StringCol(Col):
# 3-03 @@: What about BLOB?
! def __init__(self, *args, **kw):
self.length = popKey(kw, 'length')
self.varchar = popKey(kw, 'varchar', 'auto')
--- 167,191 ----
return ' '.join([self.dbName, self._sqliteType()] + self._extraSQL())
! class Col(object):
!
! baseClass = SOCol
!
! def __init__(self, name=None, **kw):
! kw['name'] = name
! kw['columnDef'] = self
! self.kw = kw
!
! def setName(self, value):
! assert kw['name'] is None, "You cannot change a name after it has already been set"
! self.kw['name'] = value
!
! def withClass(self, soClass):
! return self.baseClass(soClass=soClass, **self.kw)
!
! class SOStringCol(SOCol):
# 3-03 @@: What about BLOB?
! def __init__(self, **kw):
self.length = popKey(kw, 'length')
self.varchar = popKey(kw, 'varchar', 'auto')
***************
*** 166,170 ****
self.varchar = True
! Col.__init__(self, *args, **kw)
def autoConstraints(self):
--- 197,201 ----
self.varchar = True
! SOCol.__init__(self, **kw)
def autoConstraints(self):
***************
*** 181,186 ****
else:
return 'CHAR(%i)' % self.length
! class IntCol(Col):
# 3-03 @@: support precision, maybe max and min directly
--- 212,220 ----
else:
return 'CHAR(%i)' % self.length
+
+ class StringCol(Col):
+ baseClass = SOStringCol
! class SOIntCol(SOCol):
# 3-03 @@: support precision, maybe max and min directly
***************
*** 192,196 ****
return 'INT'
! class FloatCol(Col):
# 3-03 @@: support precision (e.g., DECIMAL)
--- 226,233 ----
return 'INT'
! class IntCol(Col):
! baseClass = SOIntCol
!
! class SOFloatCol(SOCol):
# 3-03 @@: support precision (e.g., DECIMAL)
***************
*** 202,206 ****
return 'FLOAT'
! class KeyCol(Col):
# 3-03 @@: this should have a simplified constructor
--- 239,246 ----
return 'FLOAT'
! class FloatCol(Col):
! baseClass = SOFloatCol
!
! class SOKeyCol(SOCol):
# 3-03 @@: this should have a simplified constructor
***************
*** 216,226 ****
return 'INT'
! class EnumCol(Col):
! def __init__(self, *args, **kw):
self.enumValues = popKey(kw, 'enumValues', None)
assert self.enumValues is not None, \
'You must provide an enumValues keyword argument'
! Col.__init__(self, *args, **kw)
def autoConstraints(self):
--- 256,269 ----
return 'INT'
! class KeyCol(Col):
! baseClass = SOKeyCol
! class SOEnumCol(SOCol):
!
! def __init__(self, **kw):
self.enumValues = popKey(kw, 'enumValues', None)
assert self.enumValues is not None, \
'You must provide an enumValues keyword argument'
! SOCol.__init__(self, **kw)
def autoConstraints(self):
***************
*** 239,243 ****
return self._postgresType()
! class DateTimeCol(Col):
# 3-03 @@: provide constraints; right now we let the database
--- 282,289 ----
return self._postgresType()
! class EnumCol(Col):
! baseClass = SOEnumCol
!
! class SODateTimeCol(SOCol):
# 3-03 @@: provide constraints; right now we let the database
***************
*** 250,268 ****
return 'TIMESTAMP'
! class DecimalCol(Col):
! def __init__(self, name, size, precision, **kw):
! self.size = size
! self.precision = precision
! Col.__init__(self, name, **kw)
def _sqlType(self):
return 'DECIMAL(%i, %i)' % (self.size, self.precision)
! class CurrencyCol(DecimalCol):
! def __init__(self, name, **kw):
! DecimalCol.__init__(self, name, size=10, precision=2, **kw)
def popKey(kw, name, default=None):
--- 296,325 ----
return 'TIMESTAMP'
! class DateTimeCol(Col):
! baseClass = SODateTimeCol
! class SODecimalCol(SOCol):
!
! def __init__(self, **kw):
! self.size = popKey(kw, 'size', NoDefault)
! assert self.size is not NoDefault, \
! "You must give a size argument"
! self.precision = popKey(kw, 'precision', NoDefault)
! assert self.precision is not NoDefault, \
! "You must give a precision argument"
! SOCol.__init__(self, **kw)
def _sqlType(self):
return 'DECIMAL(%i, %i)' % (self.size, self.precision)
! class DecimalCol(Col):
! baseClass = SODecimalCol
! class SOCurrencyCol(SODecimalCol):
+ def __init__(self, **kw):
+ pushKey(kw, 'size', 10)
+ pushKey(kw, 'precision', 2)
+ SODecimalCol.__init__(self, **kw)
def popKey(kw, name, default=None):
***************
*** 273,283 ****
return value
all = []
for key, value in globals().items():
! try:
! if issubclass(value, Col):
! all.append(key)
! except TypeError:
! pass
__all__ = all
--- 330,341 ----
return value
+ def pushKey(kw, name, value):
+ if not kw.has_key(name):
+ kw[name] = value
+
all = []
for key, value in globals().items():
! if isinstance(value, type) and issubclass(value, Col):
! all.append(key)
__all__ = all
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.32
retrieving revision 1.33
diff -C2 -d -r1.32 -r1.33
*** DBConnection.py 5 May 2003 17:35:19 -0000 1.32
--- DBConnection.py 6 May 2003 22:43:34 -0000 1.33
***************
*** 164,168 ****
(cls._table, cls._idName,
", ".join(["%s.%s" % (cls._table, col.dbName)
! for col in cls._columns]),
", ".join(select.tables))
--- 164,168 ----
(cls._table, cls._idName,
", ".join(["%s.%s" % (cls._table, col.dbName)
! for col in cls._SO_columns]),
", ".join(select.tables))
***************
*** 221,225 ****
columnDefs = [self.createIDColumn(soClass)] \
+ [self.createColumn(soClass, col)
! for col in soClass._columns]
return ",\n".join([" %s" % c for c in columnDefs])
--- 221,225 ----
columnDefs = [self.createIDColumn(soClass)] \
+ [self.createColumn(soClass, col)
! for col in soClass._SO_columns]
return ",\n".join([" %s" % c for c in columnDefs])
***************
*** 399,403 ****
colClass, kw = self.guessClass(t)
kw['name'] = soClass._style.dbColumnToPythonAttr(field)
! kw['notNull'] = not nullAllowed
kw['default'] = default
# @@ skip key...
--- 399,403 ----
colClass, kw = self.guessClass(t)
kw['name'] = soClass._style.dbColumnToPythonAttr(field)
! kw['notNone'] = not nullAllowed
kw['default'] = default
# @@ skip key...
***************
*** 520,524 ****
colClass, kw = self.guessClass(t)
kw['name'] = soClass._style.dbColumnToPythonAttr(field)
! kw['notNull'] = notnull
if defaultstr is not None:
kw['default'] = getattr(SQLBuilder.const, defaultstr)
--- 520,524 ----
colClass, kw = self.guessClass(t)
kw['name'] = soClass._style.dbColumnToPythonAttr(field)
! kw['notNone'] = notnull
if defaultstr is not None:
kw['default'] = getattr(SQLBuilder.const, defaultstr)
***************
*** 670,678 ****
clauses = []
for name, value in kw.items():
! clauses.append(getattr(SQLBuilder.SmartTable(soClass._table), name) == value)
return SQLBuilder.AND(*clauses)
def _SO_selectJoin(self, soClass, column, value):
results = []
for id in self._allIDs(soClass._table):
d = self._fetchDict(soClass._table, id)
--- 670,680 ----
clauses = []
for name, value in kw.items():
! clauses.append(getattr(soClass.q, name) == value)
return SQLBuilder.AND(*clauses)
def _SO_selectJoin(self, soClass, column, value):
results = []
+ # @@: seems lame I need to do this...
+ value = int(value)
for id in self._allIDs(soClass._table):
d = self._fetchDict(soClass._table, id)
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.32
retrieving revision 1.33
diff -C2 -d -r1.32 -r1.33
*** SQLObject.py 5 May 2003 20:40:37 -0000 1.32
--- SQLObject.py 6 May 2003 22:43:35 -0000 1.33
***************
*** 87,96 ****
column = Col.Col(column)
columns.append(column)
for attr, value in d.items():
if isinstance(value, Col.Col):
! value.name = attr
! columns.append(value)
del d[attr]
- d['_columns'] = columns
# We *don't* want to inherit _table, so we make sure it
--- 87,99 ----
column = Col.Col(column)
columns.append(column)
+ if columns:
+ d['_columns'] = columns
+
+ implicitColumns = []
for attr, value in d.items():
if isinstance(value, Col.Col):
! value.setName(attr)
! implicitColumns.append(value)
del d[attr]
# We *don't* want to inherit _table, so we make sure it
***************
*** 108,111 ****
--- 111,120 ----
newClass._SO_finishedClassCreation = False
+ # We append to _columns, but we don't want to change the
+ # superclass's _columns list, so we make a copy if necessary
+ if not d.has_key('_columns'):
+ newClass._columns = newClass._columns[:]
+ newClass._columns.extend(implicitColumns)
+
######################################################
# Set some attributes to their defaults, if necessary.
***************
*** 148,151 ****
--- 157,161 ----
# This is a dictionary of columnName: columnObject
newClass._SO_columnDict = {}
+ newClass._SO_columns = []
# If _table isn't given, use style default
***************
*** 288,291 ****
--- 298,303 ----
_connection = None
+ _columns = []
+
_joins = []
***************
*** 329,340 ****
return val
! def addColumn(cls, column, changeSchema=False):
! column.setClass(cls)
name = column.name
assert name != 'id', "The 'id' column is implicit, and should not be defined as a column"
cls._SO_columnDict[name] = column
! if column not in cls._columns:
! cls._columns.append(column)
###################################################
--- 341,353 ----
return val
! def addColumn(cls, columnDef, changeSchema=False):
! column = columnDef.withClass(cls)
name = column.name
assert name != 'id', "The 'id' column is implicit, and should not be defined as a column"
cls._SO_columnDict[name] = column
+ cls._SO_columns.append(column)
! if columnDef not in cls._columns:
! cls._columns.append(columnDef)
###################################################
***************
*** 435,441 ****
def addColumnsFromDatabase(cls):
! for column in cls._connection.columnsFromSchema(cls._table, cls):
! if not cls._SO_columnDict.has_key(column.name):
! cls.addColumn(column)
addColumnsFromDatabase = classmethod(addColumnsFromDatabase)
--- 448,459 ----
def addColumnsFromDatabase(cls):
! for columnDef in cls._connection.columnsFromSchema(cls._table, cls):
! alreadyExists = False
! for c in cls._columns:
! if c.kw['name'] == columnDef.kw['name']:
! alreadyExists = True
! break
! if not alreadyExists:
! cls.addColumn(columnDef)
addColumnsFromDatabase = classmethod(addColumnsFromDatabase)
***************
*** 444,448 ****
if isinstance(column, str):
column = cls._SO_columnDict[column]
! cls._columns.remove(column)
name = column.name
del cls._SO_columnDict[name]
--- 462,472 ----
if isinstance(column, str):
column = cls._SO_columnDict[column]
! if isinstance(column, Col.Col):
! for c in cls._SO_columns:
! if column is c.columnDef:
! column = c
! break
! cls._SO_columns.remove(column)
! cls._columns.remove(column.columnDef)
name = column.name
del cls._SO_columnDict[name]
***************
*** 570,574 ****
self._SO_perConnection = True
! dbNames = [col.dbName for col in self._columns]
if not selectResults:
selectResults = (connection or self._connection)._SO_selectOne(self, dbNames)
--- 594,598 ----
self._SO_perConnection = True
! dbNames = [col.dbName for col in self._SO_columns]
if not selectResults:
selectResults = (connection or self._connection)._SO_selectOne(self, dbNames)
***************
*** 629,633 ****
def _SO_selectInit(self, row):
! for col, colValue in zip(self._columns, row):
setattr(self, instanceName(col.name), colValue)
--- 653,657 ----
def _SO_selectInit(self, row):
! for col, colValue in zip(self._SO_columns, row):
setattr(self, instanceName(col.name), colValue)
***************
*** 659,663 ****
# First we do a little fix-up on the keywords we were
# passed:
! for column in inst._columns:
# If a foreign key is given, we get the ID of the object
--- 683,687 ----
# First we do a little fix-up on the keywords we were
# passed:
! for column in inst._SO_columns:
# If a foreign key is given, we get the ID of the object
***************
*** 695,699 ****
# The rest go through setattr():
for name, value in others.items():
! if not cls.__dict__.has_key(name):
raise TypeError, "%s.new() got an unexpected keyword argument %s" % (cls.__name__, name)
setattr(inst, name, value)
--- 719,725 ----
# The rest go through setattr():
for name, value in others.items():
! try:
! getattr(cls, name)
! except AttributeError:
raise TypeError, "%s.new() got an unexpected keyword argument %s" % (cls.__name__, name)
setattr(inst, name, value)
***************
*** 732,736 ****
cls,
[cls._idName] +
! [col.dbName for col in cls._columns],
dbIDName,
value)
--- 758,762 ----
cls,
[cls._idName] +
! [col.dbName for col in cls._SO_columns],
dbIDName,
value)
***************
*** 833,837 ****
def _reprItems(self):
items = []
! for col in self._columns:
value = getattr(self, col.name)
r = repr(value)
--- 859,863 ----
def _reprItems(self):
items = []
! for col in self._SO_columns:
value = getattr(self, col.name)
r = repr(value)
|