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

Reply via email to