From: Mike Bayer Date: Thu, 17 May 2012 18:18:05 +0000 (-0400) Subject: - [bug] Fixed bug whereby subqueryload() from X-Git-Tag: rel_0_8_0b1~421 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d05319ec7e7868fb09792d07d975c029c178d2d0;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - [bug] Fixed bug whereby subqueryload() from a polymorphic mapping to a target would incur a new invocation of the query for each distinct class encountered in the polymorphic result. [ticket:2480]. Also in 0.7.8. --- diff --git a/CHANGES b/CHANGES index f00eff48ff..18867b54c5 100644 --- a/CHANGES +++ b/CHANGES @@ -79,6 +79,12 @@ CHANGES set of objects that weren't modified in that sub-transaction. [ticket:2452] + - [bug] Fixed bug whereby subqueryload() from + a polymorphic mapping to a target would incur + a new invocation of the query for each + distinct class encountered in the polymorphic + result. [ticket:2480]. Also in 0.7.8. + - [bug] Fixed issue in unit of work whereby setting a non-None self-referential many-to-one relationship to None diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 4cf32335f0..a9a73cd66c 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -864,7 +864,13 @@ class SubqueryLoader(AbstractRelationshipLoader): q = context.attributes[('subquery', reduced_path)] - collections = dict( + # cache the loaded collections in the context + # so that inheriting mappers don't re-load when they + # call upon create_row_processor again + if ('collections', reduced_path) in context.attributes: + collections = context.attributes[('collections', reduced_path)] + else: + collections = context.attributes[('collections', reduced_path)] = dict( (k, [v[0] for v in v]) for k, v in itertools.groupby( q, diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py index d309a95124..70a015f29f 100644 --- a/test/orm/test_subquery_relations.py +++ b/test/orm/test_subquery_relations.py @@ -1184,4 +1184,84 @@ class SelfReferentialTest(fixtures.MappedTest): ], d) self.assert_sql_count(testing.db, go, 4) +class InheritanceToRelatedTest(fixtures.MappedTest): + @classmethod + def define_tables(cls, metadata): + Table('foo', metadata, + Column("id", Integer, primary_key=True), + Column("type", String(50)), + Column("related_id", Integer, ForeignKey("related.id")) + ) + Table("bar", metadata, + Column("id", Integer, ForeignKey('foo.id'), primary_key=True), + ) + Table("baz", metadata, + Column("id", Integer, ForeignKey('foo.id'), primary_key=True), + ) + Table("related", metadata, + Column("id", Integer, primary_key=True), + ) + + @classmethod + def setup_classes(cls): + class Foo(cls.Comparable): + pass + class Bar(Foo): + pass + class Baz(Foo): + pass + class Related(cls.Comparable): + pass + @classmethod + def fixtures(cls): + return dict( + foo = [ + ('id', 'type', 'related_id'), + (1, 'bar', 1), + (2, 'bar', 2), + (3, 'baz', 1), + (4, 'baz', 2), + ], + bar = [ + ('id', ), + (1,), + (2,) + ], + baz = [ + ('id', ), + (3,), + (4,) + ], + related = [ + ('id', ), + (1,), + (2,) + ] + ) + @classmethod + def setup_mappers(cls): + mapper(cls.classes.Foo, cls.tables.foo, properties={ + 'related':relationship(cls.classes.Related) + }, polymorphic_on=cls.tables.foo.c.type) + mapper(cls.classes.Bar, cls.tables.bar, polymorphic_identity='bar', + inherits=cls.classes.Foo) + mapper(cls.classes.Baz, cls.tables.baz, polymorphic_identity='baz', + inherits=cls.classes.Foo) + mapper(cls.classes.Related, cls.tables.related) + + def test_caches_query_per_base(self): + Foo, Bar, Baz, Related = self.classes.Foo, self.classes.Bar, \ + self.classes.Baz, self.classes.Related + s = Session(testing.db) + def go(): + eq_( + s.query(Foo).with_polymorphic([Bar, Baz]).order_by(Foo.id).options(subqueryload(Foo.related)).all(), + [ + Bar(id=1,related=Related(id=1)), + Bar(id=2,related=Related(id=2)), + Baz(id=3,related=Related(id=1)), + Baz(id=4,related=Related(id=2)) + ] + ) + self.assert_sql_count(testing.db, go, 2) \ No newline at end of file