]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- fixed bug which could occur with polymorphic "union" mapper
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 6 Jan 2008 20:32:45 +0000 (20:32 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 6 Jan 2008 20:32:45 +0000 (20:32 +0000)
which falls back to "deferred" loading of inheriting tables

- the "columns" collection on a mapper/mapped class (i.e. 'c')
is against the mapped table, not the select_table in the
case of polymorphic "union" loading (this shouldn't be
noticeable)

CHANGES
lib/sqlalchemy/orm/mapper.py
test/orm/inheritance/alltests.py
test/orm/inheritance/query.py [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 872351a13deca52a4e973f1fd2d49c0b872ed11a..20a39f51e112323fd3c5a155c41f4ec337e57367 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -18,6 +18,14 @@ CHANGES
       which are later added via add_property().  This commonly includes
       backrefs. (i.e. you can make synonyms for backrefs without
       worrying about the order of operations) [ticket:919]
+
+    - fixed bug which could occur with polymorphic "union" mapper
+      which falls back to "deferred" loading of inheriting tables
+      
+    - the "columns" collection on a mapper/mapped class (i.e. 'c')
+      is against the mapped table, not the select_table in the 
+      case of polymorphic "union" loading (this shouldn't be 
+      noticeable)
       
 - ext
     - '+', '*', '+=' and '*=' support for association proxied lists.
index e8cfc83f3352f2ca29eb33cbd22f568a19b96c96..fc62c773e939f893b6e0a2cda87ddd6ec954a1b5 100644 (file)
@@ -665,8 +665,7 @@ class Mapper(object):
                     return
 
         if isinstance(prop, ColumnProperty):
-            # relate the mapper's "select table" to the given ColumnProperty
-            col = self.select_table.corresponding_column(prop.columns[0])
+            col = self.mapped_table.corresponding_column(prop.columns[0])
             # col might not be present! the selectable given to the mapper need not include "deferred"
             # columns (included in zblog tests)
             if col is None:
@@ -681,9 +680,9 @@ class Mapper(object):
             if isinstance(prop.instrument, Mapper._CompileOnAttr):
                 prop.instrument = object.__getattribute__(prop.instrument, 'existing_prop')
             if prop.map_column:
-                if not key in self.select_table.c:
-                    raise exceptions.ArgumentError("Can't compile synonym '%s': no column on table '%s' named '%s'"  % (prop.name, self.select_table.description, key))
-                self._compile_property(prop.name, ColumnProperty(self.select_table.c[key]), init=init, setparent=setparent)    
+                if not key in self.mapped_table.c:
+                    raise exceptions.ArgumentError("Can't compile synonym '%s': no column on table '%s' named '%s'"  % (prop.name, self.mapped_table.description, key))
+                self._compile_property(prop.name, ColumnProperty(self.mapped_table.c[key]), init=init, setparent=setparent)    
         self.__props[key] = prop
 
         if setparent:
@@ -1484,6 +1483,8 @@ class Mapper(object):
             return None
 
     def _deferred_inheritance_condition(self, base_mapper, needs_tables):
+        base_mapper = base_mapper.primary_mapper()
+        
         def visit_binary(binary):
             leftcol = binary.left
             rightcol = binary.right
index dc93ed9b38a856b29043fa2e0c6397345de61f6a..d95de624609d5b446836fd96395847410093808c 100644 (file)
@@ -4,6 +4,7 @@ import unittest
 def suite():
     modules_to_test = (
         'orm.inheritance.basic',
+        'orm.inheritance.query',
         'orm.inheritance.manytomany',
         'orm.inheritance.single',
         'orm.inheritance.concrete',
diff --git a/test/orm/inheritance/query.py b/test/orm/inheritance/query.py
new file mode 100644 (file)
index 0000000..ee837ba
--- /dev/null
@@ -0,0 +1,135 @@
+"""tests the Query object's ability to work with polymorphic selectables
+and inheriting mappers."""
+
+# TODO: under construction !
+
+import testbase
+import sets
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from testlib import *
+from testlib import fixtures
+
+class Company(fixtures.Base):
+    pass
+    
+class Person(fixtures.Base):
+    pass
+class Engineer(Person):
+    pass
+class Manager(Person):
+    pass
+class Boss(Manager):
+    pass
+
+class Paperwork(fixtures.Base):
+    pass
+
+class PolymorphicQueryTest(ORMTest):
+    keep_data = True
+    keep_mappers = True
+    
+    def define_tables(self, metadata):
+        global companies, people, engineers, managers, boss, paperwork
+        
+        companies = Table('companies', metadata, 
+           Column('company_id', Integer, Sequence('company_id_seq', optional=True), primary_key=True),
+           Column('name', String(50)))
+
+        people = Table('people', metadata, 
+           Column('person_id', Integer, Sequence('person_id_seq', optional=True), primary_key=True),
+           Column('company_id', Integer, ForeignKey('companies.company_id')),
+           Column('name', String(50)),
+           Column('type', String(30)))
+
+        engineers = Table('engineers', metadata, 
+           Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
+           Column('status', String(30)),
+           Column('engineer_name', String(50)),
+           Column('primary_language', String(50)),
+          )
+
+        managers = Table('managers', metadata, 
+           Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True),
+           Column('status', String(30)),
+           Column('manager_name', String(50))
+           )
+
+        boss = Table('boss', metadata, 
+            Column('boss_id', Integer, ForeignKey('managers.person_id'), primary_key=True),
+            Column('golf_swing', String(30)),
+            )
+
+        paperwork = Table('paperwork', metadata, 
+            Column('paperwork_id', Integer, primary_key=True),
+            Column('description', String(50)), 
+            Column('person_id', Integer, ForeignKey('people.person_id')))
+            
+        # create the most awkward polymorphic selects possible; 
+        # the union does not include the "people" table by itself nor does it have
+        # "people.person_id" directly in it, and it also does not include at all
+        # the "boss" table
+        person_join = polymorphic_union(
+            {
+                'engineer':people.join(engineers),
+                'manager':people.join(managers),
+            }, None, 'pjoin')
+            
+        # separate join for second-level inherit    
+        manager_join = people.join(managers).outerjoin(boss)
+
+        mapper(Company, companies, properties={
+            'employees':relation(Person)
+        })
+        mapper(Person, people, select_table=person_join, polymorphic_on=people.c.type, polymorphic_identity='person', order_by=person_join.c.person_id, 
+            properties={
+                'paperwork':relation(Paperwork)
+            })
+        mapper(Engineer, engineers, inherits=Person, polymorphic_identity='engineer')
+        mapper(Manager, managers, select_table=manager_join, inherits=Person, polymorphic_identity='manager')
+        mapper(Boss, boss, inherits=Manager, polymorphic_identity='boss')
+        mapper(Paperwork, paperwork)
+        
+    def insert_data(self):
+        c1 = Company(name="MegaCorp, Inc.")
+        c2 = Company(name="Elbonia, Inc.")
+        e1 = Engineer(name="dilbert", engineer_name="dilbert", primary_language="java", status="regular engineer", paperwork=[
+            Paperwork(description="tps report #1"),
+            Paperwork(description="tps report #2")
+        ])
+        e2 = Engineer(name="wally", engineer_name="wally", primary_language="c++", status="regular engineer", paperwork=[
+            Paperwork(description="tps report #3"),
+            Paperwork(description="tps report #4")
+        ])
+        b1 = Boss(name="pointy haired boss", golf_swing="fore", manager_name="pointy", status="da boss", paperwork=[
+            Paperwork(description="review #1"),
+        ])
+        m1 = Manager(name="dogbert", manager_name="dogbert", status="regular manager", paperwork=[
+            Paperwork(description="review #2"),
+            Paperwork(description="review #3")
+        ])
+        c1.employees = [e1, e2, b1, m1]
+        
+        e3 = Engineer(name="vlad", engineer_name="vlad", primary_language="cobol", status="elbonian engineer")
+        c2.employees = [e3]
+        sess = create_session()
+        sess.save(c1)
+        sess.save(c2)
+        sess.flush()
+        sess.clear()
+        
+        global all_employees, c1_employees, c2_employees
+        all_employees = [e1, e2, b1, m1, e3]
+        c1_employees = [e1, e2, b1, m1]
+        c2_employees = [e3]
+    
+    def test_load_all(self):
+        sess = create_session()
+        
+        self.assertEquals(sess.query(Person).all(), all_employees)
+
+if __name__ == "__main__":    
+    testbase.main()
+        
+        
+        
\ No newline at end of file