Author: phd
Date: 2005-11-28 09:16:53 +0000 (Mon, 28 Nov 2005)
New Revision: 1326
Added:
home/phd/SQLObject/paramstyles/sqlobject/tests/test_combining_joins.py
Modified:
home/phd/SQLObject/paramstyles/sqlobject/boundattributes.py
home/phd/SQLObject/paramstyles/sqlobject/declarative.py
home/phd/SQLObject/paramstyles/sqlobject/joins.py
home/phd/SQLObject/paramstyles/sqlobject/main.py
home/phd/SQLObject/paramstyles/sqlobject/tests/dbtest.py
home/phd/SQLObject/paramstyles/sqlobject/tests/test_basic.py
home/phd/SQLObject/paramstyles/sqlobject/tests/test_events.py
Log:
Merged patches from the revisions 1314:1325 from the trunk
Modified: home/phd/SQLObject/paramstyles/sqlobject/boundattributes.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/boundattributes.py 2005-11-27 05:54:15 UTC (rev 1325)
+++ home/phd/SQLObject/paramstyles/sqlobject/boundattributes.py 2005-11-28 09:16:53 UTC (rev 1326)
@@ -27,6 +27,7 @@
'bind_attributes_local']
import declarative
+import events
class BoundAttribute(declarative.Declarative):
@@ -41,6 +42,8 @@
name, **attrs)`` and maybe ``set_object(added_class, name,
**attrs)`` (the default implementation of ``set_object``
just resets the attribute to whatever ``make_object`` returned).
+
+ Also see ``BoundFactory``.
"""
_private_variables = (
@@ -51,9 +54,11 @@
'_add_attrs',
'set_object',
'make_object',
+ 'clone_in_subclass',
)
_all_attrs = ()
+ clone_for_subclass = True
def __classinit__(cls, new_attrs):
declarative.Declarative.__classinit__(cls, new_attrs)
@@ -61,7 +66,7 @@
def __instanceinit__(self, new_attrs):
declarative.Declarative.__instanceinit__(self, new_attrs)
- self._all_attrs = self._add_attrs(self, new_attrs)
+ self.__dict__['_all_attrs'] = self._add_attrs(self, new_attrs)
def _add_attrs(this_object, new_attrs):
private = this_object._private_variables
@@ -82,6 +87,18 @@
attrs['added_class'] = added_class
attrs['attr_name'] = attr_name
obj = me.make_object(**attrs)
+
+ if self.clone_for_subclass:
+ def on_rebind(new_class_name, bases, new_attrs,
+ post_funcs, early_funcs):
+ def rebind(new_class):
+ me.set_object(
+ new_class, attr_name,
+ me.make_object(**attrs))
+ post_funcs.append(rebind)
+ events.listen(receiver=on_rebind, soClass=added_class,
+ signal=events.ClassCreateSignal, weak=False)
+
me.set_object(added_class, attr_name, obj)
__addtoclass__ = declarative.classinstancemethod(__addtoclass__)
@@ -96,29 +113,24 @@
make_object = classmethod(make_object)
+ def __setattr__(self, name, value):
+ self.__dict__['_all_attrs'] = self._add_attrs(self, {name: value})
+ self.__dict__[name] = value
+
class BoundFactory(BoundAttribute):
+ """
+ This will bind the attribute to whatever is given by
+ ``factory_class``. This factory should be a callable with the
+ signature ``factory_class(added_class, attr_name, *args, **kw)``.
+
+ The factory will be reinvoked (and the attribute rebound) for
+ every subclassing.
+ """
+
factory_class = None
+ _private_variables = (
+ BoundAttribute._private_variables + ('factory_class',))
def make_object(cls, added_class, attr_name, *args, **kw):
return cls.factory_class(added_class, attr_name, *args, **kw)
-
-def bind_attributes(cls, new_attrs):
- for name, value in new_attrs.items():
- if hasattr(value, '__addtoclass__'):
- value.__addtoclass__(cls, name)
-
-def bind_attributes_local(cls, new_attrs):
- new_bound_attributes = {}
- for name, value in getattr(cls, '__bound_attributes__', {}).items():
- if new_attrs.has_key(name):
- # The attribute is being REbound, so don't try to bind it
- # again.
- continue
- value.__addtoclass__(cls, name)
- new_bound_attributes[name] = value
- for name, value in new_attrs.items():
- if hasattr(value, '__addtoclass__'):
- value.__addtoclass__(cls, name)
- new_bound_attributes[name] = value
- cls.__bound_attributes__ = new_bound_attributes
Modified: home/phd/SQLObject/paramstyles/sqlobject/declarative.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/declarative.py 2005-11-27 05:54:15 UTC (rev 1325)
+++ home/phd/SQLObject/paramstyles/sqlobject/declarative.py 2005-11-28 09:16:53 UTC (rev 1326)
@@ -112,6 +112,8 @@
__metaclass__ = DeclarativeMeta
+ __restrict_attributes__ = None
+
def __classinit__(cls, new_attrs):
cls.declarative_count = counter.next()
for name in cls.__mutableattributes__:
@@ -119,6 +121,12 @@
setattr(cls, copy.copy(getattr(cls, name)))
def __instanceinit__(self, new_attrs):
+ if self.__restrict_attributes__ is not None:
+ for name in new_attrs:
+ if name not in self.__restrict_attributes__:
+ raise TypeError(
+ '%s() got an unexpected keyword argument %r'
+ % (self.__class__.__name__, name))
for name, value in new_attrs.items():
setattr(self, name, value)
if not new_attrs.has_key('declarative_count'):
Modified: home/phd/SQLObject/paramstyles/sqlobject/joins.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/joins.py 2005-11-27 05:54:15 UTC (rev 1325)
+++ home/phd/SQLObject/paramstyles/sqlobject/joins.py 2005-11-28 09:16:53 UTC (rev 1326)
@@ -268,43 +268,24 @@
+import boundattributes
-class ManyToMany(object):
-
- def __init__(self, otherClassName,
- intermediateTable=None,
- joinColumn=None,
- otherColumn=None,
- createJoinTable=True):
- self.otherClassName = otherClassName
- self.intermediateTable = intermediateTable
- self.joinColumn = joinColumn
- self.otherColumn = otherColumn
- self.createJoinTable = createJoinTable
-
- def __addtoclass__(self, soClass, name):
- setattr(soClass, name,
- SOManyToMany(soClass, name=name,
- otherClassName=self.otherClassName,
- intermediateTable=self.intermediateTable,
- joinColumn=self.joinColumn,
- otherColumn=self.otherColumn,
- createJoinTable=self.createJoinTable))
-
class SOManyToMany(object):
- def __init__(self, soClass, name, otherClassName,
+ def __init__(self, soClass, name, join,
intermediateTable, joinColumn, otherColumn,
- createJoinTable):
+ createJoinTable, **attrs):
self.name = name
self.intermediateTable = intermediateTable
self.joinColumn = joinColumn
self.otherColumn = otherColumn
self.createJoinTable = createJoinTable
self.soClass = self.otherClass = None
+ for name, value in attrs.items():
+ setattr(self, name, value)
classregistry.registry(
soClass.sqlmeta.registry).addClassCallback(
- otherClassName, self._setOtherClass)
+ join, self._setOtherClass)
classregistry.registry(
soClass.sqlmeta.registry).addClassCallback(
soClass.__name__, self._setThisClass)
@@ -335,6 +316,11 @@
self.soClass, events.CreateTableSignal)
events.listen(self.event_CreateTableSignal,
self.otherClass, events.CreateTableSignal)
+ self.clause = (
+ (self.otherClass.q.id ==
+ sqlbuilder.Field(self.intermediateTable, self.otherColumn))
+ & (sqlbuilder.Field(self.intermediateTable, self.joinColumn)
+ == self.soClass.q.id))
def __get__(self, obj, type):
if obj is None:
@@ -345,10 +331,7 @@
& (sqlbuilder.Field(self.intermediateTable, self.joinColumn)
== obj.id))
select = self.otherClass.select(query)
- return _ManyToManySelectWrapper(obj, self, select)
-
- def __sqlrepr__(self, dbname):
- return self.query.__sqlrepr__(self, dbname)
+ return _ManyToManySelectWrapper(obj, self, select)
def event_CreateTableSignal(self, soClass, connection, extra_sql,
post_funcs):
@@ -359,6 +342,19 @@
if connection.tableExists(self.intermediateTable):
return
connection._SO_createJoinTable(self)
+
+class ManyToMany(boundattributes.BoundFactory):
+ factory_class = SOManyToMany
+ __restrict_attributes__ = (
+ 'join', 'intermediateTable',
+ 'joinColumn', 'otherColumn', 'createJoinTable')
+ __unpackargs__ = ('join',)
+
+ # Default values:
+ intermediateTable = None
+ joinColumn = None
+ otherColumn = None
+ createJoinTable = True
class _ManyToManySelectWrapper(object):
@@ -370,7 +366,7 @@
def __getattr__(self, attr):
# @@: This passes through private variable access too... should it?
# Also magic methods, like __str__
- return getattr(self.select, attr)
+ return getattr(self, select, attr)
def __repr__(self):
return '<%s for: %s>' % (self.__class__.__name__, repr(self.select))
@@ -385,7 +381,6 @@
return self.select[key]
def add(self, obj):
- print "Add", obj, "to", self.forObject
obj._connection._SO_intermediateInsert(
self.join.intermediateTable,
self.join.joinColumn,
@@ -405,34 +400,27 @@
obj = self.join.otherClass(**kw)
self.add(obj)
return obj
-
-class OneToMany(object):
-
- def __init__(self, otherClassName, joinColumn=None):
- self.otherClassName = otherClassName
- self.joinColumn = joinColumn
-
- def __addtoclass__(self, soClass, name):
- setattr(soClass, name,
- SOOneToMany(soClass, name=name,
- otherClassName=self.otherClassName,
- joinColumn=self.joinColumn))
class SOOneToMany(object):
- def __init__(self, soClass, name, otherClassName, joinColumn):
+ def __init__(self, soClass, name, join, joinColumn, **attrs):
self.soClass = soClass
self.name = name
self.joinColumn = joinColumn
+ for name, value in attrs.items():
+ setattr(self, name, value)
classregistry.registry(
soClass.sqlmeta.registry).addClassCallback(
- otherClassName, self._setOtherClass)
+ join, self._setOtherClass)
def _setOtherClass(self, otherClass):
self.otherClass = otherClass
if not self.joinColumn:
self.joinColumn = styles.getStyle(
self.soClass).tableReference(self.soClass.sqlmeta.table)
+ self.clause = (
+ sqlbuilder.Field(self.otherClass.sqlmeta.table, self.joinColumn)
+ == self.soClass.q.id)
def __get__(self, obj, type):
if obj is None:
@@ -443,6 +431,15 @@
select = self.otherClass.select(query)
return _OneToManySelectWrapper(obj, self, select)
+class OneToMany(boundattributes.BoundFactory):
+ factory_class = SOOneToMany
+ __restrict_attributes__ = (
+ 'join', 'joinColumn')
+ __unpackargs__ = ('join',)
+
+ # Default values:
+ joinColumn = None
+
class _OneToManySelectWrapper(object):
def __init__(self, forObject, join, select):
@@ -453,7 +450,7 @@
def __getattr__(self, attr):
# @@: This passes through private variable access too... should it?
# Also magic methods, like __str__
- return getattr(self, select, attr)
+ return getattr(self.select, attr)
def __repr__(self):
return '<%s for: %s>' % (self.__class__.__name__, repr(self.select))
Modified: home/phd/SQLObject/paramstyles/sqlobject/main.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/main.py 2005-11-27 05:54:15 UTC (rev 1325)
+++ home/phd/SQLObject/paramstyles/sqlobject/main.py 2005-11-28 09:16:53 UTC (rev 1326)
@@ -701,8 +701,6 @@
def __classinit__(cls, new_attrs):
- declarative.setup_attributes(cls, new_attrs)
-
# This is true if we're initializing the SQLObject class,
# instead of a subclass:
is_base = cls.__bases__ == (object,)
@@ -800,6 +798,10 @@
if connection and not hasattr(cls, '_connection'):
cls.setConnection(connection)
+ # Now the class is in an essentially OK-state, so we can
+ # set up any magic attributes:
+ declarative.setup_attributes(cls, new_attrs)
+
# 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
Modified: home/phd/SQLObject/paramstyles/sqlobject/tests/dbtest.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/tests/dbtest.py 2005-11-27 05:54:15 UTC (rev 1325)
+++ home/phd/SQLObject/paramstyles/sqlobject/tests/dbtest.py 2005-11-28 09:16:53 UTC (rev 1326)
@@ -161,14 +161,18 @@
"""
sql = getattr(soClass, soClass._connection.dbName + 'Create',
None)
+ all_extra = []
if sql:
soClass._connection.query(sql)
else:
- sql, constraints = soClass.createTableSQL()
+ sql, extra_sql = soClass.createTableSQL()
soClass.createTable()
+ all_extra.extend(extra_sql)
cls(tableName=soClass.sqlmeta.table,
createSQL=sql,
connectionURI=soClass._connection.uri())
+ for extra_sql in all_extra:
+ soClass._connection.query(extra_sql)
install = classmethod(install)
def drop(cls, soClass):
Modified: home/phd/SQLObject/paramstyles/sqlobject/tests/test_basic.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/tests/test_basic.py 2005-11-27 05:54:15 UTC (rev 1325)
+++ home/phd/SQLObject/paramstyles/sqlobject/tests/test_basic.py 2005-11-28 09:16:53 UTC (rev 1326)
@@ -279,5 +279,6 @@
def testAsDict():
setupGetters(TestSO1)
bob = TestSO1.selectBy(name='bob')[0]
- assert bob.sqlmeta.asDict() == {'passwd': 'tbq', 'name': 'bob'}
+ assert bob.sqlmeta.asDict() == {
+ 'passwd': 'tbq', 'name': 'bob', 'id': bob.id}
Copied: home/phd/SQLObject/paramstyles/sqlobject/tests/test_combining_joins.py (from rev 1325, SQLObject/trunk/sqlobject/tests/test_combining_joins.py)
Property changes on: home/phd/SQLObject/paramstyles/sqlobject/tests/test_combining_joins.py
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: home/phd/SQLObject/paramstyles/sqlobject/tests/test_events.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/tests/test_events.py 2005-11-27 05:54:15 UTC (rev 1325)
+++ home/phd/SQLObject/paramstyles/sqlobject/tests/test_events.py 2005-11-28 09:16:53 UTC (rev 1326)
@@ -3,6 +3,24 @@
from sqlobject import events
import sys
+try:
+ sorted
+except NameError:
+ # For Python 2.3 and under:
+ def sorted(lst, cmp=None, key=None, reverse=False):
+ if key:
+ lst = [(key(i), i) for i in lst]
+ lst = lst[:]
+ if cmp:
+ lst.sort(cmp)
+ else:
+ lst.sort()
+ if key:
+ lst = [i for k, i in lst]
+ if reverse:
+ lst.reverse()
+ return lst
+
class EventTester(SQLObject):
name = StringCol()
|