]> 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:45:32 +0000 (13:45 -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

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

index 61ef96aff4ca66180ebf8544c8f60cf6d46549dd..c3c0900a1d34d889adc91085545e3f8e6b4088c5 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 3ebadd62b30c23b7bfe1cd8c578c9e705901c2f3..c8946a0c07e1b85ab17b002f66669e553cc88a32 100644 (file)
@@ -885,7 +885,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 4958bdfd5be541a930c203aed14cf100785ccfd5..03498d7ea3711f5a56ffef5bf3f9f2c83c9b3e15 100644 (file)
@@ -976,7 +976,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
@@ -1132,7 +1133,115 @@ 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(Engineer.machines).
+                            subqueryload(Machine.type)
+                    )
+
+        def go():
+            eq_(
+                q.all(),
+                [self._fixture()]
+            )
+        self.assert_sql_count(testing.db, go, 4)
 
 
 class SelfReferentialTest(fixtures.MappedTest):