Author: phd
Date: 2005-12-21 09:52:41 +0000 (Wed, 21 Dec 2005)
New Revision: 1435
Modified:
SQLObject/trunk/sqlobject/inheritance/__init__.py
SQLObject/trunk/sqlobject/main.py
Log:
A patch by alexander smishlajev: move inheritance code to InheritableSQLMeta.
Modified: SQLObject/trunk/sqlobject/inheritance/__init__.py
===================================================================
--- SQLObject/trunk/sqlobject/inheritance/__init__.py 2005-12-20 21:44:56 UTC (rev 1434)
+++ SQLObject/trunk/sqlobject/inheritance/__init__.py 2005-12-21 09:52:41 UTC (rev 1435)
@@ -1,5 +1,6 @@
from sqlobject import sqlbuilder
from sqlobject import classregistry
+from sqlobject import col
from sqlobject.main import sqlmeta, SQLObject, SelectResults, True, False, \
makeProperties, getterName, setterName
import iteration
@@ -40,9 +41,8 @@
tableRegistry[registryClass] = registryClass
for registryClass in allClasses:
if registryClass.sqlmeta.table in tablesDict:
- currentClass = registryClass
- while currentClass._parentClass:
- currentClass = currentClass._parentClass
+ currentClass = registryClass.sqlmeta.parentClass
+ while currentClass:
if tableRegistry.has_key(currentClass):
#DSM: Must keep the last parent needed
#DSM: (to limit the number of join needed)
@@ -50,19 +50,21 @@
#DSM: Remove this class as it is a parent one
#DSM: of a needed children
del tableRegistry[currentClass]
+ currentClass = currentClass.sqlmeta.parentClass
#DSM: Table registry contains only the last children
#DSM: or standalone classes
parentClause = []
for (currentClass, minParentClass) in tableRegistry.items():
- while currentClass != minParentClass and currentClass._parentClass:
- parentClass = currentClass._parentClass
+ while (currentClass != minParentClass) \
+ and currentClass.sqlmeta.parentClass:
+ parentClass = currentClass.sqlmeta.parentClass
parentClause.append(currentClass.q.id == parentClass.q.id)
currentClass = parentClass
tablesDict[currentClass.sqlmeta.table] = 1
clause = reduce(sqlbuilder.AND, parentClause, clause)
- super(InheritableSelectResults, self).__init__(sourceClass, clause, clauseTables,
- **ops)
+ super(InheritableSelectResults, self).__init__(sourceClass,
+ clause, clauseTables, **ops)
class InheritableSQLMeta(sqlmeta):
@@ -71,8 +73,8 @@
#DSM: Try to add parent properties to the current class
#DSM: Only do this once if possible at object creation and once for
#DSM: each new dynamic column to refresh the current class
- if childUpdate or soClass._parentClass:
- for col in soClass._parentClass.sqlmeta.columnList:
+ if sqlmeta.parentClass:
+ for col in sqlmeta.parentClass.sqlmeta.columnList:
cname = col.name
if cname == 'childName': continue
if cname.endswith("ID"): cname = cname[:-2]
@@ -91,7 +93,7 @@
#DSM: Update each child class if needed and existing (only for new
#DSM: dynamic column as no child classes exists at object creation)
- for c in soClass._childClasses.values():
+ for c in sqlmeta.childClasses.values():
c.sqlmeta.addColumn(columnDef, connection=connection, childUpdate=True)
addColumn = classmethod(addColumn)
@@ -102,7 +104,7 @@
#DSM: Update each child class if needed
#DSM: and delete properties for this column
- for c in soClass._childClasses.values():
+ for c in sqlmeta.childClasses.values():
delattr(c, name)
delColumn = classmethod(delColumn)
@@ -112,8 +114,8 @@
#DSM: Try to add parent properties to the current class
#DSM: Only do this once if possible at object creation and once for
#DSM: each new dynamic join to refresh the current class
- if childUpdate or soClass._parentClass:
- for join in soClass._parentClass.sqlmeta.joins:
+ if sqlmeta.parentClass:
+ for join in sqlmeta.parentClass.sqlmeta.joins:
jname = join.joinMethodName
jarn = join.addRemoveName
setattr(soClass, getterName(jname),
@@ -133,7 +135,7 @@
#DSM: Update each child class if needed and existing (only for new
#DSM: dynamic join as no child classes exists at object creation)
- for c in soClass._childClasses.values():
+ for c in sqlmeta.childClasses.values():
c.sqlmeta.addJoin(joinDef, childUpdate=True)
addJoin = classmethod(addJoin)
@@ -144,7 +146,8 @@
#DSM: Update each child class if needed
#DSM: and delete properties for this join
- for c in soClass._childClasses.values():
+ for c in sqlmeta.childClasses.values():
+ # FIXME: what is ``meth``?
delattr(c, meth)
delJoin = classmethod(delJoin)
@@ -156,6 +159,55 @@
_inheritable = True
SelectResultsClass = InheritableSelectResults
+ def __classinit__(cls, new_attrs):
+ SQLObject.__classinit__(cls, new_attrs)
+ # if we are a child class, add sqlbuilder fields from parents
+ currentClass = cls.sqlmeta.parentClass
+ while currentClass:
+ for column in currentClass.sqlmeta.columnDefinitions.values():
+ if type(column) == col.ForeignKey:
+ continue
+ setattr(cls.q, column.name,
+ getattr(currentClass.q, column.name))
+ currentClass = currentClass.sqlmeta.parentClass
+
+ # @classmethod
+ def _SO_setupSqlmeta(cls, new_attrs, is_base):
+ # Note: cannot use super(InheritableSQLObject, cls)._SO_setupSqlmeta -
+ # InheritableSQLObject is not defined when it's __classinit__
+ # is run. Cannot use SQLObject._SO_setupSqlmeta, either:
+ # the method would be bound to wrong class.
+ if cls.__name__ == "InheritableSQLObject":
+ call_super = super(cls, cls)
+ else:
+ # InheritableSQLObject must be in globals yet
+ call_super = super(InheritableSQLObject, cls)
+ call_super._SO_setupSqlmeta(new_attrs, is_base)
+ sqlmeta = cls.sqlmeta
+ sqlmeta.childClasses = {}
+ # locate parent class and register this class in it's children
+ sqlmeta.parentClass = None
+ for superclass in cls.__bases__:
+ if getattr(superclass, '_inheritable', False) \
+ and (superclass.__name__ != 'InheritableSQLObject'):
+ if sqlmeta.parentClass:
+ # already have a parent class;
+ # cannot inherit from more than one
+ raise NotImplementedError(
+ "Multiple inheritance is not implemented")
+ sqlmeta.parentClass = superclass
+ superclass.sqlmeta.childClasses[cls.__name__] = cls
+ if sqlmeta.parentClass:
+ # remove inherited column definitions
+ cls.sqlmeta.columns = {}
+ cls.sqlmeta.columnList = []
+ cls.sqlmeta.columnDefinitions = {}
+ # default inheritance child name
+ if not sqlmeta.childName:
+ sqlmeta.childName = cls.__name__
+
+ _SO_setupSqlmeta = classmethod(_SO_setupSqlmeta)
+
def get(cls, id, connection=None, selectResults=None, childResults=None, childUpdate=False):
val = super(InheritableSQLObject, cls).get(id, connection, selectResults)
@@ -166,12 +218,14 @@
if 'childName' in cls.sqlmeta.columns:
childName = val.childName
if childName is not None:
- return val._childClasses[childName].get(id, connection=connection, selectResults=childResults)
+ return cls.sqlmeta.childClasses[childName].get(id,
+ connection=connection, selectResults=childResults)
#DSM: Now, we know we are alone or the last child in a family...
#DSM: It's time to find our parents
inst = val
- while inst._parentClass and not inst._parent:
- inst._parent = inst._parentClass.get(id, connection=connection, childUpdate=True)
+ while inst.sqlmeta.parentClass and not inst._parent:
+ inst._parent = inst.sqlmeta.parentClass.get(id,
+ connection=connection, childUpdate=True)
inst = inst._parent
#DSM: We can now return ourself
return val
@@ -179,14 +233,32 @@
get = classmethod(get)
def _notifyFinishClassCreation(cls):
- if not cls.sqlmeta.columnList:
+ sqlmeta = cls.sqlmeta
+ # verify names of added columns
+ if sqlmeta.parentClass:
+ # FIXME: this does not check for grandparent column overrides
+ parentCols = sqlmeta.parentClass.sqlmeta.columns.keys()
+ for column in sqlmeta.columnList:
+ if column.name == 'childName':
+ raise AttributeError(
+ "The column name 'childName' is reserved")
+ if column.name in parentCols:
+ raise AttributeError("The column '%s' is"
+ " already defined in an inheritable parent"
+ % column.name)
+ # if this class is inheritable, add column for children distinction
+ if cls._inheritable and (cls.__name__ != 'InheritableSQLObject'):
+ sqlmeta.addColumn(col.StringCol(name='childName',
+ # limit string length to get VARCHAR and not CLOB
+ length=255, default=None))
+ if not sqlmeta.columnList:
# There are no columns - call addColumn to propagate columns
# from parent classes to children
- cls.sqlmeta.addColumn(None)
- if not cls.sqlmeta.joins:
+ sqlmeta.addColumn(None)
+ if not sqlmeta.joins:
# There are no joins - call addJoin to propagate joins
# from parent classes to children
- cls.sqlmeta.addJoin(None)
+ sqlmeta.addJoin(None)
_notifyFinishClassCreation = classmethod(_notifyFinishClassCreation)
def _create(self, id, **kw):
@@ -200,8 +272,8 @@
kw = kw['kw']
#DSM: If we are the children of an inheritable class,
#DSM: we must first create our parent
- if self._parentClass:
- parentClass = self._parentClass
+ if self.sqlmeta.parentClass:
+ parentClass = self.sqlmeta.parentClass
new_kw = {}
parent_kw = {}
for (name, value) in kw.items():
@@ -210,8 +282,9 @@
else:
new_kw[name] = value
kw = new_kw
- self._parent = parentClass(kw=parent_kw, connection=self._connection)
- self._parent.childName = self.__class__.__name__
+ parent_kw["childName"] = self.sqlmeta.childName
+ self._parent = parentClass(kw=parent_kw,
+ connection=self._connection)
id = self._parent.id
@@ -244,7 +317,7 @@
def _reprItems(self):
items = super(InheritableSQLObject, self)._reprItems()
# add parent attributes (if any)
- if self._parentClass:
+ if self.sqlmeta.parentClass:
items.extend(self._parent._reprItems())
# filter out our special column
return [item for item in items if item[0] != 'childName']
Modified: SQLObject/trunk/sqlobject/main.py
===================================================================
--- SQLObject/trunk/sqlobject/main.py 2005-12-20 21:44:56 UTC (rev 1434)
+++ SQLObject/trunk/sqlobject/main.py 2005-12-21 09:52:41 UTC (rev 1435)
@@ -213,7 +213,7 @@
__metaclass__ = declarative.DeclarativeMeta
# These attributes shouldn't be shared with superclasses:
- _unshared_attributes = ['table', 'idName', 'columns']
+ _unshared_attributes = ['table', 'idName', 'columns', 'childName']
# These are internal bookkeeping attributes; the class-level
# definition is a default for the instances, instances will
@@ -233,6 +233,11 @@
# will be true. It's false by default:
_perConnection = False
+ # Inheritance definitions:
+ parentClass = None # A reference to the parent class
+ childClasses = {} # References to child classes, keyed by childName
+ childName = None # Class name for inheritance child object creation
+
def __classinit__(cls, new_attrs):
for attr in cls._unshared_attributes:
if not new_attrs.has_key(attr):
@@ -691,10 +696,11 @@
#DSM: The _inheritable attribute controls wheter the class can by
#DSM: inherited 'logically' with a foreignKey and a back reference.
_inheritable = False # Is this class inheritable?
- _parentClass = None # A reference to the parent class
_parent = None # A reference to the parent instance
- _childClasses = {} # References to child classes
childName = None # Children name (to be able to get a subclass)
+ # moved to sqlmeta in 0.8:
+ _parentClass = _sqlmeta_attr('parentClass', 2)
+ _childClasses = _sqlmeta_attr('childClasses', 2)
# The law of Demeter: the class should not call another classes by name
SelectResultsClass = SelectResults
@@ -747,44 +753,6 @@
for j in new_attrs['_joins']:
implicitJoins.append(j)
-
- #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 superclass in cls.__bases__:
- if hasattr(superclass, '_inheritable') and superclass._inheritable and \
- superclass.__name__ <> "InheritableSQLObject":
- cls._parentClass = superclass
- cls._parent = None
- superclass._childClasses[cls.__name__] = cls
-
- #DSM: If this class is a child of a parent class, we need to do some
- #DSM: attribute check and add 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 = cls.sqlmeta.columns.keys()
- for column in implicitColumns:
- cname = column.name
- if cname == 'childName':
- raise AttributeError, \
- "The column name 'childName' is reserved"
- 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 points to our parent
- cls.sqlmeta.columns = {}
- cls.sqlmeta.columnList = []
- cls.sqlmeta.columnDefinitions = {}
- #DSM: If this is inheritable, add some default columns
- #DSM: to be able to link to children
- if hasattr(cls, '_inheritable') and cls._inheritable and \
- cls.__name__ <> "InheritableSQLObject":
- cls.sqlmeta.columnDefinitions['childName'] = col.StringCol(
- name='childName', default=None)
-
######################################################
# Set some attributes to their defaults, if necessary.
# First we get the connection:
@@ -837,15 +805,6 @@
if not is_base:
cls.q = sqlbuilder.SQLObjectTable(cls)
- #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.sqlmeta.columnDefinitions.values():
- if type(column) == col.ForeignKey: continue
- setattr(cls.q, column.name,
- getattr(currentClass.q, column.name))
-
classregistry.registry(cls.sqlmeta.registry).addClass(cls)
_style = _sqlmeta_attr('style', 2)
|