From: Mike Bayer Date: Thu, 6 Jan 2011 17:25:17 +0000 (-0500) Subject: - Fixed bug regarding "subqueryload" strategy whereby X-Git-Tag: rel_0_7b1~88 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9f3b42cbce9db3592961b375dd14f6701a37f7cf;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed bug regarding "subqueryload" strategy whereby the join would fail if using a multi-level load of the form from A->joined-subclass->C [ticket:2014] --- diff --git a/CHANGES b/CHANGES index 617e825d3a..e055c75986 100644 --- a/CHANGES +++ b/CHANGES @@ -69,6 +69,10 @@ CHANGES strategy would fail if the entity was an aliased() construct. [ticket:1964] + - Fixed bug regarding "subqueryload" strategy whereby + the join would fail if using a multi-level load + of the form from A->joined-subclass->C [ticket:2014] + - Fixed indexing of Query objects by -1. It was erroneously transformed to the empty slice -1:0 that resulted in IndexError. [ticket:1968] diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 7d3e563f41..333650ec4d 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -746,9 +746,23 @@ class SubqueryLoader(AbstractRelationshipLoader): for i in xrange(0, len(subq_path), 2) ] + # determine the immediate parent class we are joining from, + # which needs to be aliased. + if len(to_join) < 2: + # in the case of a one level eager load, this is the + # leftmost "left_alias". parent_alias = left_alias + elif subq_path[-2].isa(self.parent): + # In the case of multiple levels, retrieve + # it from subq_path[-2]. This is the same as self.parent + # in the vast majority of cases, and [ticket:2014] + # illustrates a case where sub_path[-2] is a subclass + # of self.parent + parent_alias = mapperutil.AliasedClass(subq_path[-2]) else: + # if of_type() were used leading to this relationship, + # self.parent is more specific than subq_path[-2] parent_alias = mapperutil.AliasedClass(self.parent) local_cols, remote_cols = \ diff --git a/test/orm/inheritance/test_query.py b/test/orm/inheritance/test_query.py index 61727eb4e1..b176929452 100644 --- a/test/orm/inheritance/test_query.py +++ b/test/orm/inheritance/test_query.py @@ -1249,7 +1249,7 @@ class SelfReferentialM2MTest(_base.MappedTest, AssertsCompiledSQL): assert row.left_child2 class EagerToSubclassTest(_base.MappedTest): - """Test joinedloads to subclass mappers""" + """Test eager loads to subclass mappers""" run_setup_classes = 'once' run_setup_mappers = 'once' @@ -1259,19 +1259,29 @@ class EagerToSubclassTest(_base.MappedTest): @classmethod def define_tables(cls, metadata): Table('parent', metadata, - Column('id', Integer, primary_key=True, test_needs_autoincrement=True), + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), Column('data', String(10)), ) Table('base', metadata, - Column('id', Integer, primary_key=True, test_needs_autoincrement=True), + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), Column('type', String(10)), + Column('related_id', Integer, ForeignKey('related.id')) ) Table('sub', metadata, Column('id', Integer, ForeignKey('base.id'), primary_key=True), Column('data', String(10)), - Column('parent_id', Integer, ForeignKey('parent.id'), nullable=False) + Column('parent_id', Integer, + ForeignKey('parent.id'), nullable=False) + ) + + Table('related', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('data', String(10)), ) @classmethod @@ -1286,52 +1296,109 @@ class EagerToSubclassTest(_base.MappedTest): class Sub(Base): pass + class Related(_base.ComparableEntity): + pass + @classmethod @testing.resolve_artifact_names def setup_mappers(cls): mapper(Parent, parent, properties={ - 'children':relationship(Sub) + 'children':relationship(Sub, order_by=sub.c.data) }) - mapper(Base, base, polymorphic_on=base.c.type, polymorphic_identity='b') - mapper(Sub, sub, inherits=Base, polymorphic_identity='s') + mapper(Base, base, + polymorphic_on=base.c.type, + polymorphic_identity='b', + properties={ + 'related':relationship(Related) + }) + mapper(Sub, sub, inherits=Base, + polymorphic_identity='s') + mapper(Related, related) @classmethod @testing.resolve_artifact_names def insert_data(cls): - sess = create_session() - p1 = Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]) - p2 = Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')]) + sess = Session() + r1, r2 = Related(data='r1'), Related(data='r2') + s1, s2, s3 = Sub(data='s1', related=r1), \ + Sub(data='s2', related=r2), \ + Sub(data='s3') + s4, s5 = Sub(data='s4', related=r2), Sub(data='s5') + + p1 = Parent(data='p1', children=[s1, s2, s3]) + p2 = Parent(data='p2', children=[s4, s5]) sess.add(p1) sess.add(p2) - sess.flush() + sess.commit() @testing.resolve_artifact_names def test_joinedload(self): - sess = create_session() + sess = Session() def go(): eq_( - sess.query(Parent).options(joinedload(Parent.children)).all(), + sess.query(Parent).\ + options(joinedload(Parent.children)).all(), [ - Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]), - Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')]) + Parent(data='p1', children=[ + Sub(data='s1'), + Sub(data='s2'), + Sub(data='s3') + ]), + Parent(data='p2', children=[ + Sub(data='s4'), + Sub(data='s5') + ]) ] ) self.assert_sql_count(testing.db, go, 1) @testing.resolve_artifact_names def test_contains_eager(self): - sess = create_session() + sess = Session() def go(): eq_( - sess.query(Parent).join(Parent.children).options(contains_eager(Parent.children)).\ + sess.query(Parent).join(Parent.children).\ + options(contains_eager(Parent.children)).\ order_by(Parent.data, Sub.data).all(), [ - Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]), - Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')]) + Parent(data='p1', children=[ + Sub(data='s1'), + Sub(data='s2'), + Sub(data='s3') + ]), + Parent(data='p2', children=[ + Sub(data='s4'), + Sub(data='s5') + ]) ] ) self.assert_sql_count(testing.db, go, 1) + @testing.resolve_artifact_names + def test_subq_through_related(self): + sess = Session() + def go(): + eq_( + sess.query(Parent).\ + options( + subqueryload_all(Parent.children, Sub.related) + ).\ + order_by(Parent.data).\ + all(), + [ + Parent(data='p1', children=[ + Sub(data='s1', related=Related(data='r1')), + Sub(data='s2', related=Related(data='r2')), + Sub(data='s3', related=None) + ]), + Parent(data='p2', children=[ + Sub(data='s4',related=Related(data='r2')), + Sub(data='s5',related=None) + ]) + ] + ) + self.assert_sql_count(testing.db, go, 3) + class SubClassEagerToSubClassTest(_base.MappedTest): """Test joinedloads from subclass to subclass mappers"""