Johan Dahlin wrote:
> Oleg Broytmann wrote:
>> On Wed, Oct 18, 2006 at 05:38:53PM -0300, Johan Dahlin wrote:
>>> Any hints on how to solve this?
>> I need to look into it. You know, the code is tricky...
>
> Okay, I found out what needed to be done.
>
> selectBy does not use the .q magic so we need to build up the query
> manually. It turned out to be not so easy:
>
> SQLObject.selectBy uses DBAPI._SO_columnClause which cannot handle
> inheritence, it was not designed with that in mind.
> I had to copy over parts of _SO_columnClause into InheritedSQLObject and
> build up a query which does a join on the parent classes.
>
> Tested using postgres and sqlite.
I found a couple of problems when using more than two layers of inheritance,
attaching an updated patch.
Johan
Index: external/sqlobject/inheritance/__init__.py
===================================================================
--- external/sqlobject/inheritance/__init__.py (revisão 5152)
+++ external/sqlobject/inheritance/__init__.py (cópia de trabalho)
@@ -1,6 +1,6 @@
from sqlobject import sqlbuilder
from sqlobject import classregistry
-from sqlobject.col import StringCol, ForeignKey
+from sqlobject.col import StringCol, ForeignKey, popKey
from sqlobject.main import sqlmeta, SQLObject, SelectResults, True, False, \
makeProperties, getterName, setterName
import iteration
@@ -383,14 +384,72 @@
clause, *args, **kwargs)
select = classmethod(select)
+ def _SO_prepareSelectBy(cls, conn, kw):
+ ops = {None: "IS"}
+ data = {}
+ clauses = []
+ tables = []
+
+ # Inherited attributes
+ currentClass = cls.sqlmeta.parentClass
+ while currentClass:
+ tableName = currentClass.sqlmeta.table
+ for c in currentClass.sqlmeta.columns.values():
+ name = c.name
+ if name == 'childName':
+ continue
+ if not currentClass in tables:
+ tables.append(currentClass)
+ dbName = tableName + '.' + c.dbName
+ if name in kw:
+ data[dbName] = popKey(kw, name)
+ elif c.foreignName in kw:
+ obj = popKey(kw, c.foreignName)
+ if isinstance(obj, SQLObject):
+ data[dbName] = obj.id
+ else:
+ data[dbName] = obj
+ currentClass = currentClass.sqlmeta.parentClass
+
+ clauses.extend(['%s %s %s' %
+ (dbName, ops.get(value, "="), conn.sqlrepr(value))
+ for dbName, value in data.items()])
+
+ # join in parent tables
+ # This is rather tricky, we need to tie the ids and the child names
+ # together, but it needs to be done in the right order:
+ # current -> parent
+ # parent -> parent of parent
+ # etc
+ tableName = cls.__name__
+ for table in tables:
+ for c in [table.q.childName == tableName,
+ table.q.id == cls.q.id]:
+ clauses.append(conn.sqlrepr(c))
+ tableName = table.__name__
+
+ # columns in the same class, reuse _SO_columnClause
+ normal = conn._SO_columnClause(cls, kw)
+ if normal:
+ clauses.extend(normal.split(' AND '))
+
+ if kw:
+ # pick the first key from kw to use to raise the error,
+ raise TypeError(
+ "got an unexpected keyword argument(s): %r" % kw.keys())
+
+ table_names = [table.sqlmeta.table for table in tables]
+ clause = ' AND '.join(clauses)
+ return clause, table_names
+ _SO_prepareSelectBy = classmethod(_SO_prepareSelectBy)
+
def selectBy(cls, connection=None, **kw):
- clause = []
- for name, value in kw.items():
- clause.append(getattr(cls.q, name) == value)
- clause = reduce(sqlbuilder.AND, clause)
conn = connection or cls._connection
- return cls.SelectResultsClass(cls, clause, connection=conn)
-
+ clause, table_names = cls._SO_prepareSelectBy(conn, kw)
+ return cls.SelectResultsClass(cls,
+ clauseTables=table_names,
+ clause=clause,
+ connection=conn)
selectBy = classmethod(selectBy)
def destroySelf(self):
Index: external/sqlobject/inheritance/tests/test_inherited_foreignKey.py
===================================================================
--- external/sqlobject/inheritance/tests/test_inherited_foreignKey.py
(revisão 5152)
+++ external/sqlobject/inheritance/tests/test_inherited_foreignKey.py (cópia
de trabalho)
@@ -11,12 +11,17 @@
lastName = StringCol()
note = ForeignKey("Note", default=None)
+class Paper(SQLObject):
+ content = StringCol()
+
class EmployeeWithNotes(PersonWithNotes):
_inheritable = False
+ paper = ForeignKey("Paper", default=None)
def setup():
setupClass(Note)
setupClass(PersonWithNotes)
+ setupClass(Paper)
setupClass(EmployeeWithNotes)
note = Note(text="person")
@@ -24,6 +29,9 @@
note = Note(text="employee")
EmployeeWithNotes(firstName='Project', lastName='Leader', note=note)
+ paper = Paper(content="secret")
+ EmployeeWithNotes(firstName='Senior', lastName='Clerk', paper=paper)
+ PersonWithNotes(firstName='Some', lastName='Person')
def test_inheritance():
setup()
@@ -35,3 +42,31 @@
employee = EmployeeWithNotes.get(2)
assert isinstance(employee, EmployeeWithNotes)
assert employee.note.text == "employee"
+
+ persons = PersonWithNotes.select(PersonWithNotes.q.noteID <> None)
+ assert persons.count() == 2
+
+ persons = PersonWithNotes.selectBy(noteID=person.note.id)
+ assert persons.count() == 1
+
+ persons = EmployeeWithNotes.select(PersonWithNotes.q.noteID <> None)
+ assert persons.count() == 1
+
+ persons = PersonWithNotes.selectBy(noteID=person.note.id)
+ assert persons.count() == 1
+
+ persons = PersonWithNotes.selectBy(note=person.note)
+ assert persons.count() == 1
+
+ persons = PersonWithNotes.selectBy(note=None)
+ assert persons.count() == 2
+
+ persons = EmployeeWithNotes.selectBy(paper=None)
+ assert persons.count() == 1
+
+ persons = EmployeeWithNotes.selectBy(note=employee.note,
+ paper=employee.paper)
+ assert persons.count() == 1
+
+ persons = EmployeeWithNotes.selectBy()
+ assert persons.count() == 2
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
sqlobject-discuss mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sqlobject-discuss