Author: phd
Date: 2005-02-09 13:15:16 +0000 (Wed, 09 Feb 2005)
New Revision: 590
Added:
home/phd/SQLObject/inheritance/sqlobject/boundattributes.py
home/phd/SQLObject/inheritance/sqlobject/declarative.py
home/phd/SQLObject/inheritance/sqlobject/inheritance/iteration.py
home/phd/SQLObject/inheritance/sqlobject/tests/test_boundattributes.py
home/phd/SQLObject/inheritance/sqlobject/tests/test_declarative.py
Modified:
home/phd/SQLObject/inheritance/sqlobject/col.py
home/phd/SQLObject/inheritance/sqlobject/dbconnection.py
home/phd/SQLObject/inheritance/sqlobject/include/validators.py
home/phd/SQLObject/inheritance/sqlobject/inheritance/__init__.py
home/phd/SQLObject/inheritance/sqlobject/main.py
home/phd/SQLObject/inheritance/sqlobject/sqlbuilder.py
home/phd/SQLObject/inheritance/sqlobject/sqlite/sqliteconnection.py
home/phd/SQLObject/inheritance/sqlobject/styles.py
home/phd/SQLObject/inheritance/sqlobject/tests/dbtest.py
home/phd/SQLObject/inheritance/sqlobject/tests/test_auto.py
home/phd/SQLObject/inheritance/sqlobject/tests/test_basic.py
home/phd/SQLObject/inheritance/sqlobject/tests/test_blob.py
home/phd/SQLObject/inheritance/sqlobject/tests/test_distinct.py
home/phd/SQLObject/inheritance/sqlobject/tests/test_joins.py
home/phd/SQLObject/inheritance/sqlobject/tests/test_style.py
Log:
Merged patches from revisiosn 571:589 from the trunk.
Moved InheritanceIteration from dbconnection.py to inheritance/iteration.py.
Copied: home/phd/SQLObject/inheritance/sqlobject/boundattributes.py (from rev 585, trunk/SQLObject/sqlobject/boundattributes.py)
Modified: home/phd/SQLObject/inheritance/sqlobject/col.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/col.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/col.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -888,6 +888,8 @@
"""
def fromPython(self, value, state):
+ if value is None:
+ return None
return state.soObject._connection.createBinary(value)
class SOBLOBCol(SOStringCol):
Modified: home/phd/SQLObject/inheritance/sqlobject/dbconnection.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/dbconnection.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/dbconnection.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -45,13 +45,14 @@
atexit.register(_closeConnection, weakref.ref(self))
def uri(self):
- auth = self.user or ''
+ auth = getattr(self, 'user', None) or ''
if auth:
if self.password:
auth = auth + '@' + self.password
auth = auth + ':'
else:
- assert not self.password, 'URIs cannot express passwords without usernames'
+ assert not getattr(self, 'password', None), (
+ 'URIs cannot express passwords without usernames')
uri = '%s://%s' % (self.dbName, auth)
if self.host:
uri += self.host + '/'
@@ -181,6 +182,8 @@
conn.close()
def printDebug(self, conn, s, name, type='query'):
+ if name == 'Pool' and self.debug != 'Pool':
+ return
if type == 'query':
sep = ': '
else:
@@ -541,80 +544,6 @@
def __del__(self):
self._cleanup()
-class InheritableIteration(Iteration):
- #phd: default array size for cursor.fetchmany()
- defaultArraySize = 10000
-
- def __init__(self, dbconn, rawconn, select, keepConnection=False):
- super(InheritableIteration, self).__init__(dbconn, rawconn, select, keepConnection)
- self.cursor.arraysize = self.defaultArraySize
- self._results = []
- #phd: find the index of the childName column
- childNameIdx = None
- columns = select.sourceClass._SO_columns
- for i in range(len(columns)): #phd: enumerate() is unavailable python 2.2
- if columns[i].name == "childName":
- childNameIdx = i
- break
- self._childNameIdx = childNameIdx
-
- def next(self):
- lazyColumns = self.select.ops.get('lazyColumns', 0)
- if not self._results:
- self._results = list(self.cursor.fetchmany())
- if not lazyColumns: self.fetchChildren()
- if not self._results:
- self._cleanup()
- raise StopIteration
- result = self._results[0]
- del self._results[0]
- if lazyColumns:
- obj = self.select.sourceClass.get(result[0], connection=self.dbconn)
- return obj
- else:
- id = result[0]
- if id in self._childrenResults:
- childResults = self._childrenResults[id]
- del self._childrenResults[id]
- else:
- childResults = None
- obj = self.select.sourceClass.get(id, selectResults=result[1:],
- childResults=childResults, connection=self.dbconn)
- return obj
-
- def fetchChildren(self):
- """Prefetch childrens' data
-
- Fetch childrens' data for every subclass in one big .select()
- to avoid .get() fetching it one by one.
- """
- self._childrenResults = {}
- if self._childNameIdx is None:
- return
- childIdsNames = {}
- childNameIdx = self._childNameIdx
- for result in self._results:
- childName = result[childNameIdx+1]
- if childName:
- ids = childIdsNames.get(childName)
- if ids is None:
- ids = childIdsNames[childName] = []
- ids.append(result[0])
- dbconn = self.dbconn
- rawconn = self.rawconn
- cursor = rawconn.cursor()
- registry = self.select.sourceClass._registry
- for childName, ids in childIdsNames.items():
- klass = findClass(childName, registry)
- select = klass.select(sqlbuilder.IN(sqlbuilder.SQLConstant("id"), ids))
- query = dbconn.queryForSelect(select)
- if dbconn.debug:
- dbconn.printDebug(rawconn, query, 'Select children of the class %s' % childName)
- self.dbconn._executeRetry(rawconn, cursor, query)
- for result in cursor.fetchall():
- self._childrenResults[result[0]] = result[1:]
-
-
class Transaction(object):
def __init__(self, dbConnection):
Copied: home/phd/SQLObject/inheritance/sqlobject/declarative.py (from rev 585, trunk/SQLObject/sqlobject/declarative.py)
Modified: home/phd/SQLObject/inheritance/sqlobject/include/validators.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/include/validators.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/include/validators.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -195,7 +195,7 @@
self._toPython,
self.validatePython)
- def fromPython(self, value, state):
+ def fromPython(self, value, state=None):
return self.attemptConvert(value, state,
self.validatePython,
self._fromPython,
@@ -227,7 +227,7 @@
def toPython(self, value, state=None):
return self.attemptConvert(value, state, toPython)
- def fromPython(self, value, state):
+ def fromPython(self, value, state=None):
return self.attemptConvert(value, state, fromPython)
def matchesProtocol(self, validator, state):
Modified: home/phd/SQLObject/inheritance/sqlobject/inheritance/__init__.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/inheritance/__init__.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/inheritance/__init__.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -1,11 +1,11 @@
from sqlobject import sqlbuilder
-from sqlobject import dbconnection
from sqlobject import classregistry
from sqlobject.main import SQLObject, SelectResults, True, False, makeProperties, getterName, setterName
+import iteration
class InheritableSelectResults(SelectResults):
- IterationClass = dbconnection.InheritableIteration
+ IterationClass = iteration.InheritableIteration
def __init__(self, sourceClass, clause, clauseTables=None,
**ops):
@@ -177,7 +177,7 @@
]
)
self._parent = parentClass(kw=parent_kw)
- self._parent.childName = self._className
+ self._parent.childName = self.__class__.__name__
id = self._parent.id
super(InheritableSQLObject, self)._create(id, **kw)
Added: home/phd/SQLObject/inheritance/sqlobject/inheritance/iteration.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/inheritance/iteration.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/inheritance/iteration.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -0,0 +1,76 @@
+from sqlobject import sqlbuilder
+from sqlobject.classregistry import findClass
+from sqlobject.dbconnection import Iteration
+
+class InheritableIteration(Iteration):
+ #phd: default array size for cursor.fetchmany()
+ defaultArraySize = 10000
+
+ def __init__(self, dbconn, rawconn, select, keepConnection=False):
+ super(InheritableIteration, self).__init__(dbconn, rawconn, select, keepConnection)
+ self.cursor.arraysize = self.defaultArraySize
+ self._results = []
+ #phd: find the index of the childName column
+ childNameIdx = None
+ columns = select.sourceClass._SO_columns
+ for i in range(len(columns)): #phd: enumerate() is unavailable python 2.2
+ if columns[i].name == "childName":
+ childNameIdx = i
+ break
+ self._childNameIdx = childNameIdx
+
+ def next(self):
+ lazyColumns = self.select.ops.get('lazyColumns', 0)
+ if not self._results:
+ self._results = list(self.cursor.fetchmany())
+ if not lazyColumns: self.fetchChildren()
+ if not self._results:
+ self._cleanup()
+ raise StopIteration
+ result = self._results[0]
+ del self._results[0]
+ if lazyColumns:
+ obj = self.select.sourceClass.get(result[0], connection=self.dbconn)
+ return obj
+ else:
+ id = result[0]
+ if id in self._childrenResults:
+ childResults = self._childrenResults[id]
+ del self._childrenResults[id]
+ else:
+ childResults = None
+ obj = self.select.sourceClass.get(id, selectResults=result[1:],
+ childResults=childResults, connection=self.dbconn)
+ return obj
+
+ def fetchChildren(self):
+ """Prefetch childrens' data
+
+ Fetch childrens' data for every subclass in one big .select()
+ to avoid .get() fetching it one by one.
+ """
+ self._childrenResults = {}
+ if self._childNameIdx is None:
+ return
+ childIdsNames = {}
+ childNameIdx = self._childNameIdx
+ for result in self._results:
+ childName = result[childNameIdx+1]
+ if childName:
+ ids = childIdsNames.get(childName)
+ if ids is None:
+ ids = childIdsNames[childName] = []
+ ids.append(result[0])
+ dbconn = self.dbconn
+ rawconn = self.rawconn
+ cursor = rawconn.cursor()
+ registry = self.select.sourceClass._registry
+ for childName, ids in childIdsNames.items():
+ klass = findClass(childName, registry)
+ select = klass.select(sqlbuilder.IN(sqlbuilder.SQLConstant("id"), ids))
+ query = dbconn.queryForSelect(select)
+ if dbconn.debug:
+ dbconn.printDebug(rawconn, query, 'Select children of the class %s' % childName)
+ self.dbconn._executeRetry(rawconn, cursor, query)
+ for result in cursor.fetchall():
+ self._childrenResults[result[0]] = result[1:]
Modified: home/phd/SQLObject/inheritance/sqlobject/main.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/main.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/main.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -4,14 +4,6 @@
SQLObject is a object-relational mapper. See SQLObject.html or
SQLObject.txt for more.
-Modified by
- Daniel Savard, Xsoli Inc <sqlobject xsoli.com> 7 Feb 2004
- - Added support for simple table inheritance.
-
- Oleg Broytmann, SIA "ANK" <ph...@ph...> 3 Feb 2005
- - Split inheritance support into a number of separate classes -
- InheritableSQLObject at al.
-
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
@@ -39,6 +31,7 @@
import index
import classregistry
findClass = classregistry.findClass # for those who imported findClass from sqlobject.main
+import declarative
import sys
if sys.version_info[:3] < (2, 2, 0):
@@ -51,209 +44,6 @@
True, False = 1==1, 0==1
-# This is the metaclass. It essentially takes a dictionary
-# of all the attributes (and thus methods) defined in the
-# class definition. It futzes with them, and spits out the
-# new class definition.
-class MetaSQLObject(type):
-
- def __new__(cls, className, bases, d):
-
- # 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)
- if columns:
- d['_columns'] = columns
-
- implicitColumns = []
- implicitJoins = []
- implicitIndexes = []
- for attr, value in d.items():
- if isinstance(value, col.Col):
- value.name = attr
- implicitColumns.append(value)
- del d[attr]
- continue
- if isinstance(value, joins.Join):
- value.joinMethodName = attr
- implicitJoins.append(value)
- del d[attr]
- continue
- if isinstance(value, index.DatabaseIndex):
- value.setName(attr)
- implicitIndexes.append(value)
- del d[attr]
- continue
-
- # 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
-
-
- if d.has_key('_connection'):
- connection = d['_connection']
- del d['_connection']
- assert not d.has_key('connection')
- elif d.has_key('connection'):
- connection = d['connection']
- del d['connection']
- else:
- connection = None
-
- # We actually create the class.
- newClass = type.__new__(cls, className, bases, d)
- newClass._SO_finishedClassCreation = False
-
- #DSM: Need to keep the name of the class for easy access later
- newClass._className = className
- newClass._childClasses = {}
- #DSM: Need to know very soon if the class is a child of an
- #DSM: inheritable class. If so, we keep a link to our parent class.
- for cls in bases:
- if hasattr(cls, '_inheritable') and cls._inheritable:
- newClass._parentClass = cls
- newClass._parent = None
- cls._childClasses[className] = newClass
-
- # 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[:]
- #DSM: If this class is a child of a parent class, we need to do some
- #DSM: attribute check and a a foreign key to the parent.
- if newClass._parentClass:
- #DSM: First, look for invalid column name:
- #DSM: reserved ones or same as a parent
- parentCols = [column.name for column in newClass._columns]
- for column in implicitColumns:
- cname = column.name
- if cname in ['childName']:
- raise AttributeError, \
- "The column name '%s' is reserved" % cname
- if cname in parentCols:
- raise AttributeError, "The column '%s' is already " \
- "defined in an inheritable parent" % cname
- #DSM: Remove columns if inherited from an inheritable class
- #DSM: as we don't want them. All we want is a foreign key
- #DSM: that will link to our parent
- newClass._columns = []
- #DSM: If this is inheritable, add some default columns
- # to be able to link to children
- if hasattr(newClass, '_inheritable') and newClass._inheritable:
- newClass._columns.append(
- col.StringCol(name='childName',default=None))
- newClass._columns.extend(implicitColumns)
- if not d.has_key('_joins'):
- newClass._joins = newClass._joins[:]
- newClass._joins.extend(implicitJoins)
- if not d.has_key('_indexes'):
- newClass._indexes = newClass._indexes[:]
- newClass._indexes.extend(implicitIndexes)
-
- ######################################################
- # Set some attributes to their defaults, if necessary.
- # First we get the connection:
- if not connection and not getattr(newClass, '_connection', None):
- mod = sys.modules[newClass.__module__]
- # See if there's a __connection__ global in
- # the module, use it if there is.
- if hasattr(mod, '__connection__'):
- connection = mod.__connection__
-
- if connection or not hasattr(newClass, '_connection'):
- newClass.setConnection(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 = styles.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 = {}
- newClass._SO_columns = []
-
- # 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 d.has_key('_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.SQLObjectTable(newClass)
- #DSM: If we are a child, get the q magic from the parent
- currentClass = newClass
- while currentClass._parentClass:
- currentClass = currentClass._parentClass
- for column in currentClass._columns:
- if type(column) == col.ForeignKey: continue
- setattr(newClass.q, column.name,
- getattr(currentClass.q, column.name))
-
- # We have to check if there are columns in the inherited
- # _columns where the attribute has been set to None in this
- # class. If so, then we need to remove that column from
- # _columns.
- for column in newClass._columns[:]:
- if d.has_key(column.name) and d[column.name] is None:
- newClass._columns.remove(column)
-
- 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 = []
- newClass._SO_joinDict = {}
-
- for join in newClass._joins:
- newClass.addJoin(join)
-
- # We don't setup the properties until we're finished with the
- # batch adding of all the columns...
- newClass._SO_finishedClassCreation = True
- makeProperties(newClass)
-
- newClass._SO_indexList = []
- for idx in newClass._indexes:
- newClass.addIndex(idx)
-
- classregistry.registry(newClass._registry).addClass(newClass)
-
- # And return the class
- return newClass
-
def makeProperties(obj):
"""
This function takes a dictionary of methods and finds
@@ -343,12 +133,67 @@
"""
pass
+class sqlmeta(object):
-try:
- basestring
-except NameError: # Python 2.2
- basestring = (types.StringType, types.UnicodeType)
+ """
+ This object is the object we use to keep track of all sorts of
+ information. Subclasses are made for each SQLObject subclass
+ (dynamically if necessary), and instances are created to go
+ alongside every SQLObject instance.
+ """
+ table = None
+ idName = None
+ style = None
+
+ __metaclass__ = declarative.DeclarativeMeta
+
+ # These attributes shouldn't be shared with superclasses:
+ _unshared_attributes = ['table']
+
+ def __classinit__(cls, new_attrs):
+ for attr in cls._unshared_attributes:
+ if not new_attrs.has_key(attr):
+ setattr(cls, attr, None)
+
+ def __init__(self, instance):
+ self.instance = instance
+
+ def setClass(cls, soClass):
+ cls.soClass = soClass
+ setClass = classmethod(setClass)
+
+ def finishClass(cls):
+ if not cls.style:
+ if cls.soClass._connection and cls.soClass._connection.style:
+ cls.style = cls.soClass._connection.style
+ else:
+ cls.style = styles.defaultStyle
+ if cls.table is None:
+ cls.table = cls.style.pythonClassToDBTable(cls.soClass.__name__)
+ if cls.idName is None:
+ cls.idName = cls.style.idForTable(cls.table)
+ finishClass = classmethod(finishClass)
+
+class _sqlmeta_attr(object):
+
+ def __init__(self, name):
+ self.name = name
+
+ def __get__(self, obj, type=None):
+ return getattr((type or obj).sqlmeta, self.name)
+
+
+# @@: This should become a public interface or documented or
+# something. Turning it on gives earlier warning about things
+# that will be deprecated (having this off we won't flood people
+# with warnings right away).
+strict_warnings = False
+
+def deprecated(message):
+ if strict_warnings:
+ warnings.warn(message, DeprecationWarning, stacklevel=1)
+
class SelectResults(object):
IterationClass = dbconnection.Iteration
@@ -360,10 +205,6 @@
self.clause = clause
tablesDict = sqlbuilder.tablesUsedDict(self.clause)
tablesDict[sourceClass._table] = 1
- orderBy = ops.get('orderBy')
- if orderBy and not isinstance(orderBy, basestring):
- tablesDict.update(sqlbuilder.tablesUsedDict(orderBy))
-
if clauseTables:
for table in clauseTables:
tablesDict[table] = 1
@@ -487,12 +328,12 @@
Return the accumulate result
"""
conn = self.ops.get('connection', self.sourceClass._connection)
- return conn.accumulateSelect(self,expression)
+ return conn.accumulateSelect(self, expression)
def count(self):
""" Counting elements of current select results """
assert not self.ops.get('distinct'), "It is not currently supported to count distinct objects"
-
+
count = self.accumulate('COUNT(*)')
if self.ops.get('start'):
count -= self.ops['start']
@@ -511,7 +352,6 @@
expression = sqlbuilder.func.SUM(attribute)
return self.accumulate(expression)
-
# SQLObject is the superclass for all SQLObject classes, of
# course. All the deeper magic is done in MetaSQLObject, and
# only lesser magic is done here. All the actual work is done
@@ -520,7 +360,7 @@
# MetaSQLObject.
class SQLObject(object):
- __metaclass__ = MetaSQLObject
+ __metaclass__ = declarative.DeclarativeMeta
# When an object is being created, it has an instance
# variable _SO_creating, which is true. This way all the
@@ -542,14 +382,6 @@
# it's set (default 1).
_cacheValues = True
- #DSM: The _inheritable attribute controls wheter the class can by
- #DSM: inherited 'logically' with a foreignKey and back reference.
- _inheritable = False # Does this class is inheritable
- _parentClass = None # A reference to the parent class
- _parent = None # A reference to the parent instance
- _childClasses = {} # Reference to child classes
- childName = None # Children name (to be able to get a subclass)
-
# The _defaultOrder is used by SelectResults
_defaultOrder = None
@@ -578,13 +410,233 @@
# aren't using integer IDs
_idType = int
+ sqlmeta = sqlmeta
+
+ #DSM: The _inheritable attribute controls wheter the class can by
+ #DSM: inherited 'logically' with a foreignKey and back reference.
+ _inheritable = False # Does this class is inheritable
+ _parentClass = None # A reference to the parent class
+ _parent = None # A reference to the parent instance
+ _childClasses = {} # Reference to child classes
+ childName = None # Children name (to be able to get a subclass)
+
# The law of Demeter: the class should not call another classes by name
SelectResultsClass = SelectResults
+ def __classinit__(cls, new_attrs):
+
+ # This is true if we're initializing the SQLObject class,
+ # instead of a subclass:
+ is_base = cls.__bases__ == (object,)
+
+ if (not new_attrs.has_key('sqlmeta')
+ and not is_base):
+ # We have to create our own subclass, usually.
+ # type(className, bases_tuple, attr_dict) creates a new
+ # subclass:
+ #cls.sqlmeta = cls.sqlmeta.clone()
+ cls.sqlmeta = type('sqlmeta', (cls.sqlmeta,), {})
+ cls.sqlmeta.setClass(cls)
+ cls.sqlmeta.finishClass()
+
+
+ implicitColumns = []
+ implicitJoins = []
+ implicitIndexes = []
+ for attr, value in new_attrs.items():
+ if isinstance(value, col.Col):
+ value.name = attr
+ implicitColumns.append(value)
+ delattr(cls, attr)
+ continue
+ if isinstance(value, joins.Join):
+ value.joinMethodName = attr
+ implicitJoins.append(value)
+ delattr(cls, attr)
+ continue
+ if isinstance(value, index.DatabaseIndex):
+ value.setName(attr)
+ implicitIndexes.append(value)
+ delattr(cls, attr)
+ continue
+
+ if (new_attrs.has_key('_table') and not is_base):
+ deprecated("'_table' is deprecated; please set the 'table' "
+ "attribute in sqlmeta instead")
+ cls.sqlmeta.table = cls._table
+ del cls._table
+
+ if new_attrs.has_key('_connection'):
+ connection = new_attrs['_connection']
+ del cls._connection
+ assert not new_attrs.has_key('connection')
+ elif new_attrs.has_key('connection'):
+ connection = new_attrs['connection']
+ del cls.connection
+ else:
+ connection = None
+
+ cls._SO_finishedClassCreation = False
+
+ # 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 new_attrs.get('_columns', []):
+ if isinstance(column, str):
+ column = col.Col(column)
+ columns.append(column)
+ if columns:
+ cls._columns = columns
+
+ # 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 new_attrs.has_key('_columns'):
+ cls._columns = cls._columns[:]
+
+ #DSM: Need to know very soon if the class is a child of an
+ #DSM: inheritable class. If so, we keep a link to our parent class.
+ cls._childClasses = {}
+ for _cls in cls.__bases__:
+ if hasattr(_cls, '_inheritable') and _cls._inheritable:
+ cls._parentClass = _cls
+ cls._parent = None
+ _cls._childClasses[cls.__name__] = cls
+
+ #DSM: If this class is a child of a parent class, we need to do some
+ #DSM: attribute check and a a foreign key to the parent.
+ if cls._parentClass:
+ #DSM: First, look for invalid column name:
+ #DSM: reserved ones or same as a parent
+ parentCols = [column.name for column in cls._columns]
+ for column in implicitColumns:
+ cname = column.name
+ if cname in ['childName']:
+ raise AttributeError, \
+ "The column name '%s' is reserved" % cname
+ if cname in parentCols:
+ raise AttributeError, "The column '%s' is already " \
+ "defined in an inheritable parent" % cname
+ #DSM: Remove columns if inherited from an inheritable class
+ #DSM: as we don't want them. All we want is a foreign key
+ #DSM: that will link to our parent
+ cls._columns = []
+ #DSM: If this is inheritable, add some default columns
+ # to be able to link to children
+ if hasattr(cls, '_inheritable') and cls._inheritable:
+ cls._columns.append(
+ col.StringCol(name='childName',default=None))
+
+ cls._columns.extend(implicitColumns)
+ if not new_attrs.has_key('_joins'):
+ cls._joins = cls._joins[:]
+ cls._joins.extend(implicitJoins)
+ if not new_attrs.has_key('_indexes'):
+ cls._indexes = cls._indexes[:]
+ cls._indexes.extend(implicitIndexes)
+
+ ######################################################
+ # Set some attributes to their defaults, if necessary.
+ # First we get the connection:
+ if not connection and not getattr(cls, '_connection', None):
+ mod = sys.modules[cls.__module__]
+ # See if there's a __connection__ global in
+ # the module, use it if there is.
+ if hasattr(mod, '__connection__'):
+ connection = mod.__connection__
+
+ if connection or not hasattr(cls, '_connection'):
+ cls.setConnection(connection)
+
+ if (new_attrs.has_key('_style') and not is_base):
+ deprecated("'_style' is deprecated; please set the 'style' "
+ "attribute in sqlmeta instead")
+ cls.sqlmeta.style = cls._style
+ del cls._style
+
+ # 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.
+ cls._SO_plainSetters = {}
+ cls._SO_plainGetters = {}
+ cls._SO_plainForeignSetters = {}
+ cls._SO_plainForeignGetters = {}
+ cls._SO_plainJoinGetters = {}
+ cls._SO_plainJoinAdders = {}
+ cls._SO_plainJoinRemovers = {}
+
+ # This is a dictionary of columnName: columnObject
+ cls._SO_columnDict = {}
+ cls._SO_columns = []
+
+ if (new_attrs.has_key('_idName') and not is_base):
+ deprecated("'_idName' is deprecated; please set the 'idName' "
+ "attribute in sqlmeta instead")
+ cls.sqlmeta.idName = cls._idName
+ del cls._idName
+
+ #DSM: If we are a child, get the q magic from the parent
+ currentClass = cls
+ while currentClass._parentClass:
+ currentClass = currentClass._parentClass
+ for column in currentClass._columns:
+ if type(column) == col.ForeignKey: continue
+ setattr(cls.q, column.name,
+ getattr(currentClass.q, column.name))
+
+ # We have to check if there are columns in the inherited
+ # _columns where the attribute has been set to None in this
+ # class. If so, then we need to remove that column from
+ # _columns.
+ for column in cls._columns[:]:
+ if (new_attrs.has_key(column.name)
+ and new_attrs[column.name] is None):
+ cls._columns.remove(column)
+
+ for column in cls._columns:
+ cls.addColumn(column)
+ if cls._fromDatabase:
+ cls.addColumnsFromDatabase()
+
+ ########################################
+ # Now we do the joins:
+
+ # We keep track of the different joins by index,
+ # putting them in this list.
+ cls._SO_joinList = []
+ cls._SO_joinDict = {}
+
+ for join in cls._joins:
+ cls.addJoin(join)
+
+ # We don't setup the properties until we're finished with the
+ # batch adding of all the columns...
+ cls._SO_finishedClassCreation = True
+ makeProperties(cls)
+
+ cls._SO_indexList = []
+ for idx in cls._indexes:
+ cls.addIndex(idx)
+
+ # We use the magic "q" attribute for accessing lazy
+ # SQL where-clause generation. See the sql module for
+ # more.
+ if not is_base:
+ cls.q = sqlbuilder.SQLObjectTable(cls)
+
+ classregistry.registry(cls._registry).addClass(cls)
+
+ _style = _sqlmeta_attr('style')
+ _table = _sqlmeta_attr('table')
+ _idName = _sqlmeta_attr('idName')
+
def get(cls, id, connection=None, selectResults=None):
assert id is not None, 'None is not a possible id for %s' % cls.__name
-
+
id = cls._idType(id)
if connection is None:
@@ -662,7 +714,7 @@
setattr(cls, '_SO_toPython_%s' % name, column.toPython)
setattr(cls, rawSetterName(name), setter)
# Then do the aliasing
- if not hasattr(cls, setterName(name)) or name == 'childName':
+ if not hasattr(cls, setterName(name)) or (name == 'childName'):
setattr(cls, setterName(name), setter)
# We keep track of setters that haven't been
# overridden, because we can combine these
@@ -775,7 +827,7 @@
conn.delColumn(cls._table, column)
if cls._SO_finishedClassCreation:
- delattr(cls, name)
+ unmakeProperties(cls)
delColumn = classmethod(delColumn)
@@ -853,7 +905,7 @@
delattr(cls, 'add' + join.addRemovePrefix)
if cls._SO_finishedClassCreation:
- delattr(cls, meth)
+ unmakeProperties(cls)
delJoin = classmethod(delJoin)
@@ -863,6 +915,10 @@
# created, unlike __init__ which would be called
# anytime the object was returned from cache.
self.id = id
+ # We shadow the sqlmeta class with an instance of sqlmeta
+ # that points to us (our sqlmeta buddy object; where the
+ # sqlmeta class is our class's buddy class)
+ self.sqlmeta = self.__class__.sqlmeta(self)
self._SO_writeLock = threading.Lock()
# If no connection was given, we'll inherit the class
@@ -1119,6 +1175,7 @@
self._create(id, **kw)
def _create(self, id, **kw):
+
self._SO_creating = True
self._SO_createValues = {}
self._SO_validatorState = SQLObjectState(self)
@@ -1393,7 +1450,6 @@
cls._connection = value
setConnection = classmethod(setConnection)
-
def capitalize(name):
return name[0].capitalize() + name[1:]
Modified: home/phd/SQLObject/inheritance/sqlobject/sqlbuilder.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/sqlbuilder.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/sqlbuilder.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -329,6 +329,9 @@
def __init__(self, soClass):
self.soClass = soClass
+ assert soClass._table, (
+ "Bad table name in class %r: %r"
+ % (soClass, soClass._table))
Table.__init__(self, soClass._table)
def __getattr__(self, attr):
Modified: home/phd/SQLObject/inheritance/sqlobject/sqlite/sqliteconnection.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/sqlite/sqliteconnection.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/sqlite/sqliteconnection.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -23,11 +23,18 @@
def connectionFromURI(cls, uri):
user, password, host, path, args = cls._parseURI(uri)
- assert host is None, "SQLite can only be used locally (with a URI like sqlite:///file or sql:/file, not %r)" % uri
- assert user is None and password is None, "You may not provide usernames or passwords for SQLite databases"
+ assert host is None, (
+ "SQLite can only be used locally (with a URI like "
+ "sqlite:///file or sqlite:/file, not %r)" % uri)
+ assert user is None and password is None, (
+ "You may not provide usernames or passwords for SQLite "
+ "databases")
return cls(filename=path, **args)
connectionFromURI = classmethod(connectionFromURI)
+ def uri(self):
+ return 'sqlite:///%s' % self.filename
+
def _setAutoCommit(self, conn, auto):
conn.autocommit = auto
Modified: home/phd/SQLObject/inheritance/sqlobject/styles.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/styles.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/styles.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -58,6 +58,9 @@
# keys, you can't really change this style.
return attr + "ID"
+ def tableReference(self, table):
+ return table + "_id"
+
class MixedCaseUnderscoreStyle(Style):
"""
Modified: home/phd/SQLObject/inheritance/sqlobject/tests/dbtest.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/tests/dbtest.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/tests/dbtest.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -43,17 +43,26 @@
}
-def setupClass(soClass):
+def setupClass(soClasses):
"""
- Makes sure the class has a corresponding and correct table.
- This won't recreate the table if it already exists. It will
- check that the table is properly defined (in case you change
- your table definition).
+ Makes sure the classes have a corresponding and correct table.
+ This won't recreate the table if it already exists. It will check
+ that the table is properly defined (in case you change your table
+ definition).
+
+ You can provide a single class or a list of classes; if a list
+ then classes will be created in the order you provide, and
+ destroyed in the opposite order. So if class A depends on class
+ B, then do setupClass([B, A]) and B won't be destroyed or cleared
+ until after A is destroyed or cleared.
"""
+ if not isinstance(soClasses, (list, tuple)):
+ soClasses = [soClasses]
connection = getConnection()
- soClass._connection = connection
- installOrClear(soClass)
- return soClass
+ for soClass in soClasses:
+ soClass._connection = connection
+ installOrClear(soClasses)
+ return soClasses
installedDBFilename = os.path.join(os.getcwd(), 'dbs_data.tmp')
@@ -82,27 +91,33 @@
createSQL = sqlobject.StringCol(notNull=True)
connectionURI = sqlobject.StringCol(notNull=True)
- def installOrClear(cls, soClass):
+ def installOrClear(cls, soClasses):
cls.setup()
- table = soClass._table
- if not soClass._connection.tableExists(table):
- cls.install(soClass)
- items = list(cls.selectBy(
- tableName=table,
- connectionURI=soClass._connection.uri()))
- if items:
- instance = items[0]
- sql = instance.createSQL
- else:
- sql = None
- newSQL = soClass.createTableSQL()
- if sql != newSQL:
- if sql is not None:
- instance.destroySelf()
- cls.drop(soClass)
- cls.install(soClass)
- else:
- cls.clear(soClass)
+ reversed = list(soClasses)[:]
+ reversed.reverse()
+ for soClass in reversed:
+ table = soClass._table
+ if not soClass._connection.tableExists(table):
+ continue
+ items = list(cls.selectBy(
+ tableName=table,
+ connectionURI=soClass._connection.uri()))
+ if items:
+ instance = items[0]
+ sql = instance.createSQL
+ else:
+ sql = None
+ newSQL = soClass.createTableSQL()
+ if sql != newSQL:
+ if sql is not None:
+ instance.destroySelf()
+ cls.drop(soClass)
+ else:
+ cls.clear(soClass)
+ for soClass in soClasses:
+ table = soClass._table
+ if not soClass._connection.tableExists(table):
+ cls.install(soClass)
installOrClear = classmethod(installOrClear)
def install(cls, soClass):
Modified: home/phd/SQLObject/inheritance/sqlobject/tests/test_auto.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/tests/test_auto.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/tests/test_auto.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -73,6 +73,7 @@
age INT DEFAULT NULL,
created DATETIME NOT NULL,
happy char(1) DEFAULT 'Y' NOT NULL,
+ long_field TEXT,
wannahavefun TINYINT DEFAULT 0 NOT NULL
)
"""
@@ -85,6 +86,7 @@
age INT DEFAULT 0,
created VARCHAR(40) NOT NULL,
happy char(1) DEFAULT 'Y' NOT NULL,
+ long_field TEXT,
wannahavefun BOOL DEFAULT FALSE NOT NULL
)
"""
@@ -96,7 +98,8 @@
last_name VARCHAR(200) NOT NULL,
age INT DEFAULT 0,
created VARCHAR(40) NOT NULL,
- happy char(1) DEFAULT 'Y' NOT NULL
+ happy char(1) DEFAULT 'Y' NOT NULL,
+ long_field TEXT
)
"""
@@ -125,12 +128,16 @@
lastName='doe',
age=10,
created=now(),
- wannahavefun=False)
+ wannahavefun=False,
+ long_field='x'*1000)
jane = AutoTest(firstName='jane',
lastName='doe',
happy='N',
created=now(),
- wannahavefun=True)
+ wannahavefun=True,
+ long_field='x'*1000)
assert not john.wannahavefun
assert jane.wannahavefun
+ assert john.long_field == 'x'*1000
+ assert jane.long_field == 'x'*1000
del classregistry.registry(AutoTest._registry).classes['AutoTest']
Modified: home/phd/SQLObject/inheritance/sqlobject/tests/test_basic.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/tests/test_basic.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/tests/test_basic.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -77,8 +77,7 @@
me = StringCol(length=10)
def test_foreignKey():
- setupClass(TestSO3)
- setupClass(TestSO4)
+ setupClass([TestSO4, TestSO3])
tc3 = TestSO3(name='a')
assert tc3.other is None
assert tc3.other2 is None
Modified: home/phd/SQLObject/inheritance/sqlobject/tests/test_blob.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/tests/test_blob.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/tests/test_blob.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -19,4 +19,7 @@
ImageData._connection.cache.clear()
prof2 = ImageData.get(iid)
+ # @@: This seems to fail in SQLite, which trims off the
+ # first and last character (\x00 and \xff). We should write
+ # a test for the upstream driver, as the error might be there.
assert prof2.image == data
Copied: home/phd/SQLObject/inheritance/sqlobject/tests/test_boundattributes.py (from rev 585, trunk/SQLObject/sqlobject/tests/test_boundattributes.py)
Copied: home/phd/SQLObject/inheritance/sqlobject/tests/test_declarative.py (from rev 585, trunk/SQLObject/sqlobject/tests/test_declarative.py)
Modified: home/phd/SQLObject/inheritance/sqlobject/tests/test_distinct.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/tests/test_distinct.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/tests/test_distinct.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -18,8 +18,7 @@
return result
def test_distinct():
- setupClass(Distinct1)
- setupClass(Distinct2)
+ setupClass([Distinct1, Distinct2])
obs = [Distinct1(n=i) for i in range(3)]
Distinct2(other=obs[0])
Distinct2(other=obs[0])
Modified: home/phd/SQLObject/inheritance/sqlobject/tests/test_joins.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/tests/test_joins.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/tests/test_joins.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -76,8 +76,7 @@
class TestJoin2:
def setup_method(self, meth):
- setupClass(PersonJoiner2)
- setupClass(AddressJoiner2)
+ setupClass([PersonJoiner2, AddressJoiner2])
p1 = PersonJoiner2(name='bob')
p2 = PersonJoiner2(name='sally')
for z in ['11111', '22222', '33333']:
Modified: home/phd/SQLObject/inheritance/sqlobject/tests/test_style.py
===================================================================
--- home/phd/SQLObject/inheritance/sqlobject/tests/test_style.py 2005-02-09 08:37:37 UTC (rev 589)
+++ home/phd/SQLObject/inheritance/sqlobject/tests/test_style.py 2005-02-09 13:15:16 UTC (rev 590)
@@ -19,8 +19,7 @@
_style = AnotherStyle()
def test_style():
- setupClass(SOStyleTest2)
- setupClass(SOStyleTest1)
+ setupClass([SOStyleTest2, SOStyleTest1])
st1 = SOStyleTest1(a='something', st2=None)
st2 = SOStyleTest2(b='whatever')
st1.st2 = st2
|