Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv32496/SQLObject
Modified Files:
Col.py DBConnection.py SQLObject.py
Log Message:
* Reorganized SQLObjectMeta.__new__ (simplified)
* New Style module, that allows different ways to map Python attributes
to database names (instead of columnName -> column_name)
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -d -r1.13 -r1.14
*** Col.py 21 Apr 2003 07:40:26 -0000 1.13
--- Col.py 21 Apr 2003 22:37:16 -0000 1.14
***************
*** 19,23 ****
class Col(object):
! def __init__(self, name, dbName=None, default=NoDefault,
foreignKey=None,
alternateID=False, alternateMethodName=None,
--- 19,23 ----
class Col(object):
! def __init__(self, name=None, dbName=None, default=NoDefault,
foreignKey=None,
alternateID=False, alternateMethodName=None,
***************
*** 27,70 ****
# around column names, but why would anyone *want* to
# use a name like that?
assert SQLBuilder.sqlIdentifier(name), 'Name must be SQL-safe (letters, numbers, underscores): %s' \
% repr(name)
assert name != 'id', 'The column name "id" is reserved for SQLObject use (and is implicitly created).'
! # .name is public
self.name = name
# 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 = self.generateDBName(self.name)
else:
self.dbName = dbName
- self._default = default
# alternateID means that this is a unique column that
# can be used to identify rows
! self.alternateID = alternateID
! if alternateID and alternateMethodName is None:
! self.alternateMethodName = 'by' + name[0].capitalize() + name[1:]
! else:
! self.alternateMethodName = alternateMethodName
! self.foreignKey = foreignKey
! if foreignKey:
! assert name.upper().endswith('ID'), "All foreign key columns must end with 'ID' (%s)" % repr(name)
! self.foreignName = name[:-2]
else:
self.foreignName = None
- constraints = constraints or []
- if type(constraints) not in (type([]), type(())):
- constraints = [constraints]
- constraints = self.autoConstraints() + constraints
- self.notNull = notNull
- if notNull:
- constraints = [Constraints.notNull] + constraints
- self.contraints = constraints
- if unique is NoDefault:
- self.unique = alternateID
- else:
- self.unique = unique
def autoConstraints(self):
--- 27,77 ----
# around column names, but why would anyone *want* to
# use a name like that?
+ # @@: I suppose we could actually add backquotes to the
+ # dbName if we needed to...
assert SQLBuilder.sqlIdentifier(name), 'Name must be SQL-safe (letters, numbers, underscores): %s' \
% 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
+ self._default = default
+
+ 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)
else:
self.dbName = dbName
# 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)
! self.foreignName = self.name[:-2]
else:
self.foreignName = None
def autoConstraints(self):
***************
*** 266,277 ****
del kw[name]
return value
-
- _generatePythonNameRE = re.compile('_.')
- def generatePythonName(name):
- if name.endswith('_id'):
- return generatePythonName(name[:-3] + "ID")
- def subber(match):
- return match.group(0)[1].upper()
- return _generatePythonNameRE.sub(subber, name)
all = []
--- 273,276 ----
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.22
retrieving revision 1.23
diff -C2 -d -r1.22 -r1.23
*** DBConnection.py 21 Apr 2003 07:40:51 -0000 1.22
--- DBConnection.py 21 Apr 2003 22:37:16 -0000 1.23
***************
*** 44,48 ****
class DBConnection:
! def __init__(self, name=None, debug=False, cache=True):
if name:
assert not _connections.has_key(name), 'A database by the name %s has already been created: %s' % (name, _connections[name])
--- 44,49 ----
class DBConnection:
! def __init__(self, name=None, debug=False, cache=True,
! style=None):
if name:
assert not _connections.has_key(name), 'A database by the name %s has already been created: %s' % (name, _connections[name])
***************
*** 51,54 ****
--- 52,56 ----
self.debug = debug
self.cache = CacheSet(cache=cache)
+ self.style = style
***************
*** 393,397 ****
column.dbName))
! def columnsFromSchema(self, tableName):
colData = self.queryAll("SHOW COLUMNS FROM %s"
% tableName)
--- 395,399 ----
column.dbName))
! def columnsFromSchema(self, tableName, soClass):
colData = self.queryAll("SHOW COLUMNS FROM %s"
% tableName)
***************
*** 401,405 ****
continue
colClass, kw = self.guessClass(t)
! kw['name'] = Col.generatePythonName(field)
kw['notNull'] = not nullAllowed
kw['default'] = default
--- 403,407 ----
continue
colClass, kw = self.guessClass(t)
! kw['name'] = soClass._style.dbColumnToPythonAttr(field)
kw['notNull'] = not nullAllowed
kw['default'] = default
***************
*** 493,497 ****
column.dbName))
! def columnsFromSchema(self, tableName):
keyQuery = """
--- 495,499 ----
column.dbName))
! def columnsFromSchema(self, tableName, soClass):
keyQuery = """
***************
*** 525,529 ****
continue
colClass, kw = self.guessClass(t)
! kw['name'] = Col.generatePythonName(field)
kw['notNull'] = notnull
if defaultstr is not None:
--- 527,531 ----
continue
colClass, kw = self.guessClass(t)
! kw['name'] = soClass._style.dbColumnToPythonAttr(field)
kw['notNull'] = notnull
if defaultstr is not None:
***************
*** 589,592 ****
--- 591,601 ----
# turn it into a boolean:
return not not result
+
+ def iterSelect(self, select):
+ """
+ SQLite doesn't support concurrent access, so we do the entire
+ SELECT right away and iterate over a list of objects.
+ """
+ return iter(list(DBAPI.iterSelect(self, select)))
########################################
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.25
retrieving revision 1.26
diff -C2 -d -r1.25 -r1.26
*** SQLObject.py 19 Apr 2003 03:31:11 -0000 1.25
--- SQLObject.py 21 Apr 2003 22:37:16 -0000 1.26
***************
*** 25,28 ****
--- 25,29 ----
import DBConnection
import Col
+ import Style
from util import splitWords
***************
*** 74,186 ****
class MetaSQLObject(type):
! def __new__(cls, className, bases, dict):
global classRegistry, needSet
! # plainSetters are columns that haven't been
! # overridden by the user, so we can contact the
! # database directly to set them.
! dict['_SO_plainSetters'] = {}
! dict['_SO_plainGetters'] = {}
! dict['_SO_plainForeignSetters'] = {}
! dict['_SO_plainForeignGetters'] = {}
! dict['_SO_plainJoinGetters'] = {}
! dict['_SO_plainJoinAdders'] = {}
! dict['_SO_plainJoinRemovers'] = {}
!
! # This is a dictionary of columnName: columnObject
! dict['_SO_columnDict'] = {}
!
! # The _cacheValues attribute controls if you cache
! # values fetched from the database. We make sure
! # it's set (default 1).
! cacheValues = dict.get('_cacheValues', 1)
! dict['_cacheValues'] = cacheValues
!
! # The _defaultOrder is used by SelectResults
! if not dict.has_key('_defaultOrder'):
! dict['_defaultOrder'] = None
!
! # We fix up the columns here -- replacing any
! # strings with simply-contructed Col objects,
! # and making sure each column knows where it
! # comes from (the className).
columns = []
! for column in dict.get('_columns', []):
if isinstance(column, str):
column = Col.Col(column)
columns.append(column)
! column.className = className
! dict['_columns'] = columns
######################################################
# Set some attributes to their defaults, if necessary.
# First we get the connection:
! if not dict.has_key('_connection'):
! # The SQLObject class is special for these defaults,
! # since it's abstract we'll ignore it.
! if className != 'SQLObject':
! mod = sys.modules[dict['__module__']]
! # See if there's a __connection__ global in
! # the module, use it if there is.
! if hasattr(mod, '__connection__'):
! dict['_connection'] = mod.__connection__
! # If _table isn't given, just use the class name
! # (with mixedCase turned to mixed_case).
! if not dict.has_key('_table'):
! dict['_table'] = generateTableName(className)
! # If _idName isn't given, just use "id"
! if not dict.has_key('_idName'):
! dict['_idName'] = 'id'
! ########################################
! # Now we do the joins:
! # We keep track of the different joins by index,
! # putting them in this list.
! dict['_SO_joinList'] = []
! # Joins are generally many-to-many, or many-to-one where
! # this class is the "one", so there's no associated
! # column.
! if not dict.has_key('_joins'):
! dict['_joins'] = []
# We use the magic "q" attribute for accessing lazy
# SQL where-clause generation. See the sql module for
# more.
! dict['q'] = SQLBuilder.SmartTable(dict['_table'])
!
! # If the connection is named, we turn the name into
! # a real connection.
! if dict.has_key('_connection') and \
! isinstance(dict['_connection'], str):
! dict['_connection'] = DBConnection.connectionForName(dict['_connection'])
!
! # needSet stuff (see top of module) would get messed
! # up if more than one SQLObject class has the same
! # name.
! assert not classRegistry.has_key(className), "A database object by the name %s has already been created" % repr(className)
!
! # We actually create the class.
! newClass = type.__new__(cls, className, bases, dict)
! newClass._SO_finishedClassCreation = False
for column in newClass._columns[:]:
newClass.addColumn(column)
! if dict.get('_fromDatabase'):
newClass.addColumnsFromDatabase()
! for join in newClass._joins[:]:
! newClass.addJoin(join)
! # Register it, for use with needSet
! classRegistry[className] = newClass
! # Call needSet
! setNeedSet()
# We don't setup the properties until we're finished with the
--- 75,180 ----
class MetaSQLObject(type):
! def __new__(cls, className, bases, d):
global classRegistry, needSet
! # We fix up the columns here -- replacing any strings with
! # simply-contructed Col objects, and searching the class
! # variables for instances of Col objects (which get put into
! # the _columns instance variable and deleted).
columns = []
! for column in d.get('_columns', []):
if isinstance(column, str):
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
! # is defined in this class (not a superclass)
! if not d.has_key('_table'):
! d['_table'] = None
!
! # needSet stuff (see top of module) would get messed
! # up if more than one SQLObject class has the same
! # name.
! assert not classRegistry.has_key(className), "A database object by the name %s has already been created" % repr(className)
!
! # We actually create the class.
! newClass = type.__new__(cls, className, bases, d)
! newClass._SO_finishedClassCreation = False
######################################################
# Set some attributes to their defaults, if necessary.
# First we get the connection:
! if not newClass._connection:
! mod = sys.modules[newClass.__module__]
! # See if there's a __connection__ global in
! # the module, use it if there is.
! if hasattr(mod, '__connection__'):
! newClass._connection = mod.__connection__
! # If the connection is named, we turn the name into
! # a real connection.
! if isinstance(newClass._connection, str):
! newClass._connection = DBConnection.connectionForName(
! newClass._connection)
! # The style object tells how to map between Python
! # identifiers and Database identifiers:
! if not newClass._style:
! if newClass._connection and newClass._connection.style:
! newClass._style = newClass._connection.style
! else:
! newClass._style = Style.defaultStyle
! # plainSetters are columns that haven't been overridden by the
! # user, so we can contact the database directly to set them.
! # Note that these can't set these in the SQLObject class
! # itself, because they specific to this subclass of SQLObject,
! # and cannot be shared among classes.
! newClass._SO_plainSetters = {}
! newClass._SO_plainGetters = {}
! newClass._SO_plainForeignSetters = {}
! newClass._SO_plainForeignGetters = {}
! newClass._SO_plainJoinGetters = {}
! newClass._SO_plainJoinAdders = {}
! newClass._SO_plainJoinRemovers = {}
! # This is a dictionary of columnName: columnObject
! newClass._SO_columnDict = {}
! # If _table isn't given, use style default
! if not newClass._table:
! newClass._table = newClass._style.pythonClassToDBTable(className)
!
! # If _idName isn't given, use style default
! if not hasattr(newClass, '_idName'):
! newClass._idName = newClass._style.idForTable(newClass._table)
# We use the magic "q" attribute for accessing lazy
# SQL where-clause generation. See the sql module for
# more.
! newClass.q = SQLBuilder.SmartTable(newClass._table)
for column in newClass._columns[:]:
newClass.addColumn(column)
! if newClass._fromDatabase:
newClass.addColumnsFromDatabase()
! ########################################
! # Now we do the joins:
! # We keep track of the different joins by index,
! # putting them in this list.
! newClass._SO_joinList = []
! for join in newClass._joins:
! newClass.addJoin(join)
# We don't setup the properties until we're finished with the
***************
*** 189,192 ****
--- 183,191 ----
makeProperties(newClass)
+ # Register it, for use with needSet
+ classRegistry[className] = newClass
+ # Call needSet
+ setNeedSet()
+
# And return the class
return newClass
***************
*** 250,262 ****
break
- def generateTableName(s):
- """
- Given a mixed case class name, like SomeClass, turn it into
- a underscore-separated name like some_class.
- """
- return s[0].lower() + splitWords(s[1:])
-
def findClass(name):
! assert classRegistry.has_key(name), "No class by the name %s found" % repr(name)
return classRegistry[name]
--- 249,254 ----
break
def findClass(name):
! assert classRegistry.has_key(name), "No class by the name %s found (I have %s)" % (repr(name), ', '.join(classRegistry.keys()))
return classRegistry[name]
***************
*** 280,288 ****
# left.
_SO_creating = False
# Sometimes an intance is attached to a connection, not
# globally available. In that case, self._SO_perConnection
# will be true. It's false by default:
! _SO_perConnection=False
def __new__(cls, id, connection=None):
--- 272,297 ----
# left.
_SO_creating = False
+ _SO_obsolete = False
# Sometimes an intance is attached to a connection, not
# globally available. In that case, self._SO_perConnection
# will be true. It's false by default:
! _SO_perConnection = False
!
! # The _cacheValues attribute controls if you cache
! # values fetched from the database. We make sure
! # it's set (default 1).
! _cacheValues = False
!
! # The _defaultOrder is used by SelectResults
! _defaultOrder = None
!
! _connection = None
!
! _joins = []
!
! _fromDatabase = False
!
! _style = None
def __new__(cls, id, connection=None):
***************
*** 320,323 ****
--- 329,333 ----
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"
***************
*** 426,430 ****
def addColumnsFromDatabase(cls):
! for column in cls._connection.columnsFromSchema(cls._table):
if not cls._SO_columnDict.has_key(column.name):
cls.addColumn(column)
--- 436,440 ----
def addColumnsFromDatabase(cls):
! for column in cls._connection.columnsFromSchema(cls._table, cls):
if not cls._SO_columnDict.has_key(column.name):
cls.addColumn(column)
***************
*** 476,480 ****
# The join sometimes needs to know who we are,
# mostly to generate some internal names:
! join.initCallingClass(cls.__name__, cls._table)
# Now that the join is set up, we add it to our
--- 486,490 ----
# The join sometimes needs to know who we are,
# mostly to generate some internal names:
! join.initCallingClass(cls)
# Now that the join is set up, we add it to our
***************
*** 552,557 ****
self._SO_autoInitDone = False
self._SO_writeLock = threading.Lock()
- # _SO_obsolete means we were deleted:
- self._SO_obsolete = False
# If no connection was given, we'll inherit the class
# instance variable which should have a _connection
--- 562,565 ----
***************
*** 711,715 ****
# The rest go through setattr():
for name, value in others.items():
! if not hasattr(inst, name):
raise TypeError, "%s.new() got an unexpected keyword argument %s" % (cls.__name__, name)
setattr(inst, name, value)
--- 719,723 ----
# 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)
***************
*** 888,892 ****
self.joinMethodName = joinMethodName
! def initCallingClass(self, callingClass, dbName):
# Since some of the automatic generation of the names
# depends on the class/table to which this join belongs,
--- 896,900 ----
self.joinMethodName = joinMethodName
! def initCallingClass(self, callingClass):
# Since some of the automatic generation of the names
# depends on the class/table to which this join belongs,
***************
*** 895,904 ****
# been created.
self.callingClass = callingClass
! self.callingClassDBName = dbName
if not self.joinColumn:
# Here we set up the basic join, which is
# one-to-many, where the other class points to
# us.
! self.joinColumn = dbName + "_id"
def hasIntermediateTable(self):
--- 903,912 ----
# been created.
self.callingClass = callingClass
! self.callingClassDBName = callingClass._table
if not self.joinColumn:
# Here we set up the basic join, which is
# one-to-many, where the other class points to
# us.
! self.joinColumn = Style.getStyle(callingClass).tableReference(callingClass._table)
def hasIntermediateTable(self):
***************
*** 946,962 ****
MultipleJoin.__init__(self, otherClass, **kw)
self.intermediateTable = intermediateTable
! if otherColumn:
! self.otherColumn = otherColumn
! else:
! self.otherColumn = generateTableName(otherClass) + "_id"
def hasIntermediateTable(self):
return True
! def initCallingClass(self, callingClass, dbName):
! MultipleJoin.initCallingClass(self, callingClass, dbName)
if not self.intermediateTable:
! names = [dbName,
! generateTableName(self.otherClass)]
names.sort()
self.intermediateTable = "%s_%s" % (names[0], names[1])
--- 954,970 ----
MultipleJoin.__init__(self, otherClass, **kw)
self.intermediateTable = intermediateTable
! self.otherColumn = otherColumn
def hasIntermediateTable(self):
return True
! def initCallingClass(self, callingClass):
! MultipleJoin.initCallingClass(self, callingClass)
! if not self.otherColumn:
! self.otherColumn = Style.getStyle(
! callingClass.__name__).pythonClassToDBTableReference(self.otherClass)
if not self.intermediateTable:
! names = [callingClass._table,
! Style.getStyle(callingClass).pythonClassToDBTable(self.otherClass)]
names.sort()
self.intermediateTable = "%s_%s" % (names[0], names[1])
***************
*** 964,968 ****
def performJoin(self, inst):
cls = findClass(self.otherClass)
! me = findClass(self.callingClass)
ids = me._connection._SO_intermediateJoin(
self.intermediateTable,
--- 972,976 ----
def performJoin(self, inst):
cls = findClass(self.otherClass)
! me = self.callingClass
ids = me._connection._SO_intermediateJoin(
self.intermediateTable,
***************
*** 973,977 ****
def remove(self, inst, other):
! me = findClass(self.callingClass)
me._connection._SO_intermediateDelete(
self.intermediateTable,
--- 981,985 ----
def remove(self, inst, other):
! me = self.callingClass
me._connection._SO_intermediateDelete(
self.intermediateTable,
***************
*** 982,986 ****
def add(self, inst, other):
! me = findClass(self.callingClass)
me._connection._SO_intermediateInsert(
self.intermediateTable,
--- 990,994 ----
def add(self, inst, other):
! me = self.callingClass
me._connection._SO_intermediateInsert(
self.intermediateTable,
|