Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv3682
Modified Files:
Col.py SQLObject.py __init__.py
Added Files:
Join.py
Log Message:
* Moved joins to their own module
* Created SOJoin/Join distinction
* Joins can be defined like columns, i.e., without using _joins.
--- NEW FILE: Join.py ---
from SQLBuilder import NoDefault
import Style
import SQLObject
__all__ = ['MultipleJoin', 'RelatedJoin']
class Join(object):
def __init__(self, otherClass=None, **kw):
kw['otherClass'] = otherClass
kw['joinDef'] = self
self.kw = kw
def setName(self, value):
assert self.kw.get('joinMethodName') is None or self.kw['joinMethodName'] == value, "You have already given an explicit joinMethodName (%s), and you are now setting it to %s" % (self.kw['joinMethodName'], value)
self.kw['joinMethodName'] = value
def withClass(self, soClass):
return self.baseClass(soClass=soClass, **self.kw)
# A join is separate from a foreign key, i.e., it is
# many-to-many, or one-to-many where the *other* class
# has the foreign key.
class SOJoin(object):
def __init__(self,
soClass=None,
otherClass=None,
joinColumn=None,
joinMethodName=None,
orderBy=NoDefault,
joinDef=None):
self.soClass = soClass
self.otherClassName = otherClass
SQLObject.addNeedSet(self, otherClass, soClass._registry,
'otherClass')
self.joinColumn = joinColumn
self.joinMethodName = joinMethodName
self.orderBy = orderBy
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(self.soClass).tableReference(self.soClass._table)
def hasIntermediateTable(self):
return False
def _applyOrderBy(self, results, defaultSortClass):
if self.orderBy is NoDefault:
self.orderBy = defaultSortClass._defaultOrder
if self.orderBy is not None:
def sorter(a, b, attr=self.orderBy):
return cmp(getattr(a, attr),
getattr(b, attr))
results.sort(sorter)
return results
# This is a one-to-many
class SOMultipleJoin(SOJoin):
def __init__(self, addRemoveName=None, **kw):
# addRemovePrefix is something like @@
SOJoin.__init__(self, **kw)
# Here we generate the method names
if not self.joinMethodName:
name = self.otherClassName[0].lower() + self.otherClassName[1:]
if name.endswith('s'):
name = name + "es"
else:
name = name + "s"
self.joinMethodName = name
if not addRemoveName:
self.addRemoveName = capitalize(self.otherClassName)
else:
self.addRemoveName = addRemoveName
def performJoin(self, inst):
ids = inst._connection._SO_selectJoin(
self.otherClass,
self.joinColumn,
inst.id)
return self._applyOrderBy([self.otherClass(id) for (id,) in ids], self.otherClass)
class MultipleJoin(Join):
baseClass = SOMultipleJoin
# This is a many-to-many join, with an intermediary table
class SORelatedJoin(SOMultipleJoin):
def __init__(self,
otherColumn=None,
intermediateTable=None, **kw):
SOMultipleJoin.__init__(self, **kw)
if not otherColumn:
self.otherColumn = self.soClass._style.pythonClassToDBTableReference(self.otherClassName)
else:
self.otherColumn = otherColumn
if not intermediateTable:
names = [self.soClass._table,
self.soClass._style.pythonClassToDBTable(self.otherClassName)]
names.sort()
self.intermediateTable = '%s_%s' % (names[0], names[1])
else:
self.intermediateTable = intermediateTable
def hasIntermediateTable(self):
return True
def performJoin(self, inst):
ids = inst._connection._SO_intermediateJoin(
self.intermediateTable,
self.otherColumn,
self.joinColumn,
inst.id)
return self._applyOrderBy([self.otherClass(id) for (id,) in ids], self.otherClass)
def remove(self, inst, other):
inst._connection._SO_intermediateDelete(
self.intermediateTable,
self.joinColumn,
SQLObject.getID(inst),
self.otherColumn,
SQLObject.getID(other))
def add(self, inst, other):
inst._connection._SO_intermediateInsert(
self.intermediateTable,
self.joinColumn,
SQLObject.getID(inst),
self.otherColumn,
SQLObject.getID(other))
class RelatedJoin(MultipleJoin):
baseClass = SORelatedJoin
def capitalize(name):
return name[0].capitalize() + name[1:]
Index: Col.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v
retrieving revision 1.21
retrieving revision 1.22
diff -C2 -d -r1.21 -r1.22
*** Col.py 12 May 2003 01:58:21 -0000 1.21
--- Col.py 26 May 2003 21:48:54 -0000 1.22
***************
*** 177,181 ****
def setName(self, value):
! assert self.kw['name'] is None, "You cannot change a name after it has already been set"
self.kw['name'] = value
--- 177,181 ----
def setName(self, value):
! assert self.kw['name'] is None, "You cannot change a name after it has already been set (from %s to %s)" % (self.kw['name'], value)
self.kw['name'] = value
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.39
retrieving revision 1.40
diff -C2 -d -r1.39 -r1.40
*** SQLObject.py 25 May 2003 02:39:45 -0000 1.39
--- SQLObject.py 26 May 2003 21:48:54 -0000 1.40
***************
*** 28,31 ****
--- 28,32 ----
import types
import warnings
+ import Join
import sys
***************
*** 75,78 ****
--- 76,94 ----
newNeedList.append((needClass, setCls))
needSet[registryName] = newNeedList
+
+ def setNeedSet():
+ global needSet
+ for registryName, needClassDict in needSet.items():
+ newNeedClassDict = {}
+ for needClass, q in needClassDict.items():
+ try:
+ cls = findClass(needClass, registry=registryName)
+ for obj, attr in q:
+ setattr(obj, attr, cls)
+ except KeyError:
+ newNeedClassDict[needClass] = q
+ needSet[registryName] = newNeedClassDict
+
+
def addNeedSet(needClass, setCls):
***************
*** 80,83 ****
--- 96,111 ----
(needClass.__name__, setCls))
+
+ def addNeedSet(obj, setCls, registry, attr):
+ try:
+ cls = findClass(setCls, registry=registry)
+ setattr(obj, attr, cls)
+ return
+ except KeyError:
+ pass
+ q = needSet.setdefault(registry, {}).setdefault(setCls, [])
+ q.append((obj, attr))
+
+
# This is the metaclass. It essentially takes a dictionary
# of all the attributes (and thus methods) defined in the
***************
*** 103,106 ****
--- 131,135 ----
implicitColumns = []
+ implicitJoins = []
for attr, value in d.items():
if isinstance(value, Col.Col):
***************
*** 108,111 ****
--- 137,146 ----
implicitColumns.append(value)
del d[attr]
+ continue
+ if isinstance(value, Join.Join):
+ value.setName(attr)
+ implicitJoins.append(value)
+ del d[attr]
+ continue
# We *don't* want to inherit _table, so we make sure it
***************
*** 134,137 ****
--- 169,175 ----
newClass._columns = newClass._columns[:]
newClass._columns.extend(implicitColumns)
+ if not d.has_key('_joins'):
+ newClass._joins = newClass._joins[:]
+ newClass._joins.extend(implicitJoins)
######################################################
***************
*** 201,204 ****
--- 239,243 ----
# putting them in this list.
newClass._SO_joinList = []
+ newClass._SO_joinDict = {}
for join in newClass._joins:
***************
*** 278,282 ****
def findClass(name, registry=None):
! assert classRegistry.get(registry, {}).has_key(name), "No class by the name %s found (I have %s)" % (repr(name), ', '.join(classRegistry.keys()))
return classRegistry[registry][name]
--- 317,321 ----
def findClass(name, registry=None):
! #assert classRegistry.get(registry, {}).has_key(name), "No class by the name %s found (I have %s)" % (repr(name), ', '.join(map(str, classRegistry.keys())))
return classRegistry[registry][name]
***************
*** 454,458 ****
# some point. See needSet at the top of the
# file for more on this.
! addNeedSet(cls, column.foreignKey)
if column.alternateMethodName:
--- 493,498 ----
# some point. See needSet at the top of the
# file for more on this.
! addNeedSet(cls, column.foreignKey, cls._registry,
! '_SO_class_%s' % column.foreignKey)
if column.alternateMethodName:
***************
*** 516,539 ****
delColumn = classmethod(delColumn)
! def addJoin(cls, join):
# The name of the method we'll create. If it's
# automatically generated, it's generated by the
# join class.
meth = join.joinMethodName
- if join not in cls._joins:
- cls._joins.append(join)
-
- # The method name for adding a joined object:
- appendMeth = meth[0].upper() + meth[1:]
-
- # 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
- # list of joins:
cls._SO_joinList.append(join)
index = len(cls._SO_joinList)-1
# The function fetches the join by index, and
--- 556,571 ----
delColumn = classmethod(delColumn)
! def addJoin(cls, joinDef):
# The name of the method we'll create. If it's
# automatically generated, it's generated by the
# join class.
+ join = joinDef.withClass(cls)
meth = join.joinMethodName
+ cls._SO_joinDict[joinDef] = join
cls._SO_joinList.append(join)
index = len(cls._SO_joinList)-1
+ if joinDef not in cls._joins:
+ cls._joins.append(joinDef)
# The function fetches the join by index, and
***************
*** 555,561 ****
# standard naming trick.
func = eval('lambda self, obj: self._SO_joinList[%i].remove(self, obj)' % index)
! setattr(cls, '_SO_remove' + join.addRemovePrefix, func)
! if not hasattr(cls, 'remove' + appendMeth):
! setattr(cls, 'remove' + join.addRemovePrefix, func)
cls._SO_plainJoinRemovers[meth] = 1
--- 587,593 ----
# standard naming trick.
func = eval('lambda self, obj: self._SO_joinList[%i].remove(self, obj)' % index)
! setattr(cls, '_SO_remove' + join.addRemoveName, func)
! if not hasattr(cls, 'remove' + join.addRemoveName):
! setattr(cls, 'remove' + join.addRemoveName, func)
cls._SO_plainJoinRemovers[meth] = 1
***************
*** 565,571 ****
# And again...
func = eval('lambda self, obj: self._SO_joinList[%i].add(self, obj)' % (len(cls._SO_joinList)-1))
! setattr(cls, '_SO_add' + join.addRemovePrefix, func)
! if not hasattr(cls, 'add' + appendMeth):
! setattr(cls, 'add' + join.addRemovePrefix, func)
cls._SO_plainJoinAdders[meth] = 1
--- 597,603 ----
# And again...
func = eval('lambda self, obj: self._SO_joinList[%i].add(self, obj)' % (len(cls._SO_joinList)-1))
! setattr(cls, '_SO_add' + join.addRemoveName, func)
! if not hasattr(cls, 'add' + join.addRemoveName):
! setattr(cls, 'add' + join.addRemoveName, func)
cls._SO_plainJoinAdders[meth] = 1
***************
*** 575,583 ****
addJoin = classmethod(addJoin)
! def delJoin(cls, join):
meth = join.joinMethodName
! cls._joins.remove(join)
for i in range(len(cls._SO_joinList)):
! if cls._SO_joinList[i] is join:
cls._SO_joinList[i] = None
delattr(cls, rawGetterName(meth))
--- 607,619 ----
addJoin = classmethod(addJoin)
! def delJoin(cls, joinDef):
! join = cls._SO_joinDict[joinDef]
meth = join.joinMethodName
! cls._joins.remove(joinDef)
! del cls._SO_joinDict[joinDef]
for i in range(len(cls._SO_joinList)):
! if cls._SO_joinList[i] is joinDef:
! # Have to leave None, because we refer to joins
! # by index.
cls._SO_joinList[i] = None
delattr(cls, rawGetterName(meth))
***************
*** 825,829 ****
def createJoinTables(cls, ifNotExists=False):
! for join in cls._joins:
if not join.hasIntermediateTable():
continue
--- 861,867 ----
def createJoinTables(cls, ifNotExists=False):
! for join in cls._SO_joinList:
! if not join:
! continue
if not join.hasIntermediateTable():
continue
***************
*** 832,836 ****
# arbitrarily create it while we're creating the
# alphabetically earlier class.
! if join.callingClass > join.otherClass:
continue
if ifNotExists and \
--- 870,874 ----
# arbitrarily create it while we're creating the
# alphabetically earlier class.
! if join.soClass.__name__ > join.otherClass.__name__:
continue
if ifNotExists and \
***************
*** 842,849 ****
def dropJoinTables(cls, ifExists=False):
! for join in cls._joins:
if not join.hasIntermediateTable():
continue
! if join.callingClass > join.otherClass:
continue
if ifExists and \
--- 880,889 ----
def dropJoinTables(cls, ifExists=False):
! for join in cls._SO_joinList:
! if not join:
! continue
if not join.hasIntermediateTable():
continue
! if join.soClass.__name__ > join.otherClass.__name__:
continue
if ifExists and \
***************
*** 904,1035 ****
- ########################################
- ## Joins
- ########################################
-
- # A join is separate from a foreign key, i.e., it is
- # many-to-many, or one-to-many where the *other* class
- # has the foreign key.
- class Join(object):
-
- def __init__(self, otherClass, joinColumn=None, joinMethodName=None,
- orderBy=NoDefault):
- self.otherClass = otherClass
- self.joinColumn = joinColumn
- self.joinMethodName = joinMethodName
- self.orderBy = orderBy
-
- def initCallingClass(self, callingClass):
- # Since some of the automatic generation of the names
- # depends on the class/table to which this join belongs,
- # we can only resolve some of this after the class
- # has been created, which is after this join has
- # 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):
- return False
-
- def _applyOrderBy(self, results, defaultSortClass):
- if self.orderBy is NoDefault:
- self.orderBy = defaultSortClass._defaultOrder
- if self.orderBy is not None:
- def sorter(a, b, attr=self.orderBy):
- return cmp(getattr(a, attr),
- getattr(b, attr))
- results.sort(sorter)
- return results
-
- # This is a one-to-many
- class MultipleJoin(Join):
-
- def __init__(self, *args, **kw):
- # addRemovePrefix is something like @@
- if kw.has_key('addRemovePrefix'):
- self.addRemovePrefix = kw['addRemovePrefix']
- del kw['addRemovePrefix']
- else:
- self.addRemovePrefix = None
- Join.__init__(self, *args, **kw)
-
- # Here we generate the method names
- if not self.joinMethodName:
- name = self.otherClass[0].lower() + self.otherClass[1:]
- if name.endswith('s'):
- name = name + "es"
- else:
- name = name + "s"
- self.joinMethodName = name
- if not self.addRemovePrefix:
- self.addRemovePrefix = capitalize(self.otherClass)
-
- def performJoin(self, inst):
- # We only have names of classes a lot of the time,
- # so we have to fetch the actual class definition:
- cls = findClass(self.otherClass, registry=inst._registry)
- ids = inst._connection._SO_selectJoin(
- cls,
- self.joinColumn,
- inst.id)
- return self._applyOrderBy([cls(id) for (id,) in ids], cls)
-
- # This is a many-to-many join, with an intermediary table
- class RelatedJoin(MultipleJoin):
-
- def __init__(self, otherClass,
- otherColumn=None,
- intermediateTable=None, **kw):
- 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])
-
- def performJoin(self, inst):
- cls = findClass(self.otherClass, registry=inst._registry)
- me = self.callingClass
- ids = inst._connection._SO_intermediateJoin(
- self.intermediateTable,
- self.otherColumn,
- self.joinColumn,
- inst.id)
- return self._applyOrderBy([cls(id) for (id,) in ids], cls)
-
- def remove(self, inst, other):
- me = self.callingClass
- inst._connection._SO_intermediateDelete(
- self.intermediateTable,
- self.joinColumn,
- getID(inst),
- self.otherColumn,
- getID(other))
-
- def add(self, inst, other):
- me = self.callingClass
- inst._connection._SO_intermediateInsert(
- self.intermediateTable,
- self.joinColumn,
- getID(inst),
- self.otherColumn,
- getID(other))
-
class SelectResults(object):
--- 944,947 ----
***************
*** 1162,1165 ****
__all__ = ['NoDefault', 'SQLObject',
! 'MultipleJoin', 'RelatedJoin', 'getID', 'getObject',
'SQLObjectNotFound']
--- 1074,1077 ----
__all__ = ['NoDefault', 'SQLObject',
! 'getID', 'getObject',
'SQLObjectNotFound']
Index: __init__.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/__init__.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** __init__.py 21 Apr 2003 22:50:09 -0000 1.3
--- __init__.py 26 May 2003 21:48:54 -0000 1.4
***************
*** 4,5 ****
--- 4,6 ----
from DBConnection import *
from Style import *
+ from Join import *
|