]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug in subquery eager loading where a long chain of
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 25 May 2014 17:45:32 +0000 (13:45 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 25 May 2014 17:47:56 +0000 (13:47 -0400)
eager loads across a polymorphic-subclass boundary in conjunction
with polymorphic loading would fail to locate the subclass-link in the
chain, erroring out with a missing property name on an
:class:`.AliasedClass`. fixes #3055
- adjust the test from 1.0/0.9 to not use chained eager load style

doc/build/changelog/changelog_08.rst
lib/sqlalchemy/orm/strategies.py
test/orm/inheritance/_poly_fixtures.py
test/orm/test_subquery_relations.py

index 743c2e8878d73de31b4804e69133c83e48c93121..9115f1bc607c5fb95a9d575d9be68bb4a3c5162d 100644 (file)
 .. changelog::
     :version: 0.8.7
 
+    .. change::
+        :tags: bug, orm
+        :versions: 0.9.5, 1.0.0
+        :tickets: 3055
+
+        Fixed bug in subquery eager loading where a long chain of
+        eager loads across a polymorphic-subclass boundary in conjunction
+        with polymorphic loading would fail to locate the subclass-link in the
+        chain, erroring out with a missing property name on an
+        :class:`.AliasedClass`.
+
     .. change::
         :tags: bug, ext
         :versions: 0.9.5
index 0b003b52e2841edbcccc73a9b8169f42b0872c54..f5d14a6d28d0ff0512954255b3fbd1d069821edc 100644 (file)
@@ -895,7 +895,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
                     attr = getattr(parent_alias, key).\
                                     of_type(effective_entity)
                 else:
-                    attr = key
+                    attr = getattr(mapper.entity, key)
 
             if second_to_last:
                 q = q.join(parent_alias, attr, from_joinpoint=True)
index 1370c4cf8b3ff8aa1c05e6ae2014c4b7968a5532..689d19d338c91143e0a97a38a84f678906ef8d18 100644 (file)
@@ -23,6 +23,8 @@ class Boss(Manager):
     pass
 class Machine(fixtures.ComparableEntity):
     pass
+class MachineType(fixtures.ComparableEntity):
+    pass
 class Paperwork(fixtures.ComparableEntity):
     pass
 
index 047a4c59c3c88afe49f7034da9bfcd1c7f1c5453..4116591b2864e763d678110898a4ce065b1e1453 100644 (file)
@@ -978,7 +978,8 @@ class OrderBySecondaryTest(fixtures.MappedTest):
         self.assert_sql_count(testing.db, go, 2)
 
 
-from .inheritance._poly_fixtures import _Polymorphic, Person, Engineer, Paperwork
+from .inheritance._poly_fixtures import _Polymorphic, Person, Engineer, \
+                Paperwork, Machine, MachineType, Company
 
 class BaseRelationFromJoinedSubclassTest(_Polymorphic):
     @classmethod
@@ -1134,7 +1135,118 @@ class BaseRelationFromJoinedSubclassTest(_Polymorphic):
             )
         )
 
+class SubRelationFromJoinedSubclassMultiLevelTest(_Polymorphic):
+    @classmethod
+    def define_tables(cls, metadata):
+        Table('companies', metadata,
+            Column('company_id', Integer,
+                primary_key=True,
+                test_needs_autoincrement=True),
+            Column('name', String(50)))
+
+        Table('people', metadata,
+            Column('person_id', Integer,
+                primary_key=True,
+                test_needs_autoincrement=True),
+            Column('company_id', ForeignKey('companies.company_id')),
+            Column('name', String(50)),
+            Column('type', String(30)))
+
+        Table('engineers', metadata,
+            Column('engineer_id', ForeignKey('people.person_id'),
+                            primary_key=True),
+            Column('primary_language', String(50)))
+
+        Table('machines', metadata,
+            Column('machine_id',
+                 Integer, primary_key=True,
+                 test_needs_autoincrement=True),
+            Column('name', String(50)),
+            Column('engineer_id', ForeignKey('engineers.engineer_id')),
+            Column('machine_type_id',
+                            ForeignKey('machine_type.machine_type_id')))
+
+        Table('machine_type', metadata,
+            Column('machine_type_id',
+                    Integer, primary_key=True,
+                        test_needs_autoincrement=True),
+            Column('name', String(50)))
+
+    @classmethod
+    def setup_mappers(cls):
+        companies = cls.tables.companies
+        people = cls.tables.people
+        engineers = cls.tables.engineers
+        machines = cls.tables.machines
+        machine_type = cls.tables.machine_type
+
+        mapper(Company, companies, properties={
+                'employees': relationship(Person, order_by=people.c.person_id)
+                })
+        mapper(Person, people,
+            polymorphic_on=people.c.type,
+            polymorphic_identity='person',
+            with_polymorphic='*')
+
+        mapper(Engineer, engineers,
+            inherits=Person,
+            polymorphic_identity='engineer', properties={
+                'machines': relationship(Machine,
+                                        order_by=machines.c.machine_id)
+            })
+
+        mapper(Machine, machines, properties={
+                'type': relationship(MachineType)
+                })
+        mapper(MachineType, machine_type)
+
+
+    @classmethod
+    def insert_data(cls):
+        c1 = cls._fixture()
+        sess = create_session()
+        sess.add(c1)
+        sess.flush()
+
+    @classmethod
+    def _fixture(cls):
+        mt1 = MachineType(name='mt1')
+        mt2 = MachineType(name='mt2')
+        return Company(
+                employees=[
+                    Engineer(
+                        name='e1',
+                        machines=[
+                            Machine(name='m1', type=mt1),
+                            Machine(name='m2', type=mt2)
+                        ]
+                    ),
+                    Engineer(
+                        name='e2',
+                        machines=[
+                            Machine(name='m3', type=mt1),
+                            Machine(name='m4', type=mt1)
+                        ]
+                    )
+                ])
 
+    def test_chained_subq_subclass(self):
+        s = Session()
+        q = s.query(Company).options(
+                        subqueryload(Company.employees.of_type(Engineer)),
+                        subqueryload(Company.employees.of_type(Engineer),
+                                                            Engineer.machines),
+                        subqueryload(Company.employees.of_type(Engineer),
+                                                            Engineer.machines,
+                                                            Machine.type)
+                    )
+
+        def go():
+            eq_(
+                q.all(),
+                [self._fixture()]
+            )
+        self.assert_sql_count(testing.db, go, 4)
 
 
 class SelfReferentialTest(fixtures.MappedTest):