From: Mike Bayer Date: Sun, 25 May 2014 17:45:32 +0000 (-0400) Subject: - Fixed bug in subquery eager loading where a long chain of X-Git-Tag: rel_0_8_7~22 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5cf8dbeb5efd2b610b1fd9c03fad4791645c3c87;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - 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`. fixes #3055 - adjust the test from 1.0/0.9 to not use chained eager load style --- diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index 743c2e8878..9115f1bc60 100644 --- a/doc/build/changelog/changelog_08.rst +++ b/doc/build/changelog/changelog_08.rst @@ -11,6 +11,17 @@ .. 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 diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 0b003b52e2..f5d14a6d28 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -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) diff --git a/test/orm/inheritance/_poly_fixtures.py b/test/orm/inheritance/_poly_fixtures.py index 1370c4cf8b..689d19d338 100644 --- a/test/orm/inheritance/_poly_fixtures.py +++ b/test/orm/inheritance/_poly_fixtures.py @@ -23,6 +23,8 @@ class Boss(Manager): pass class Machine(fixtures.ComparableEntity): pass +class MachineType(fixtures.ComparableEntity): + pass class Paperwork(fixtures.ComparableEntity): pass diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py index 047a4c59c3..4116591b28 100644 --- a/test/orm/test_subquery_relations.py +++ b/test/orm/test_subquery_relations.py @@ -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):