From: Mike Bayer Date: Mon, 27 Apr 2020 20:51:43 +0000 (-0400) Subject: Alias the onclause if ORM join is to same polymorphic selectable X-Git-Tag: rel_1_4_0b1~360^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=483a644959f396e3abdcb8f0f373936569958970;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Alias the onclause if ORM join is to same polymorphic selectable Fixed bug where using :func:`.with_polymorphic` as the target of a join via :meth:`.RelationshipComparator.of_type` on a mapper that already has a subquery-based with_polymorphic setting that's equivalent to the one requested would not correctly alias the ON clause in the join. Fixes: #5288 Change-Id: I0212a990ee67a344c87fe21833bf47fdb72ca0cc --- diff --git a/doc/build/changelog/unreleased_13/5288.rst b/doc/build/changelog/unreleased_13/5288.rst new file mode 100644 index 0000000000..d5fbdf5af1 --- /dev/null +++ b/doc/build/changelog/unreleased_13/5288.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, orm + :tickets: 5288 + + Fixed bug where using :func:`.with_polymorphic` as the target of a join via + :meth:`.RelationshipComparator.of_type` on a mapper that already has a + subquery-based with_polymorphic setting that's equivalent to the one + requested would not correctly alias the ON clause in the join. diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index ab49a4dccc..98e9970786 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -2762,7 +2762,6 @@ class Query(Generative): elif prop: # joining to selectable with a mapper property given # as the ON clause - if not right_selectable.is_derived_from( right_mapper.persist_selectable ): diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index 6ac56a324a..85f23d1598 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -2377,7 +2377,10 @@ class RelationshipProperty(StrategizedProperty): if self._is_self_referential and source_selectable is None: dest_selectable = dest_selectable._anonymous_fromclause() aliased = True - elif dest_selectable is not self.mapper._with_polymorphic_selectable: + elif ( + dest_selectable is not self.mapper._with_polymorphic_selectable + or self.mapper.with_polymorphic + ): aliased = True dest_mapper = of_type_mapper or self.mapper diff --git a/test/orm/inheritance/_poly_fixtures.py b/test/orm/inheritance/_poly_fixtures.py index c9cde7a81c..5d23e7801c 100644 --- a/test/orm/inheritance/_poly_fixtures.py +++ b/test/orm/inheritance/_poly_fixtures.py @@ -150,6 +150,16 @@ class _PolymorphicFixtureBase(fixtures.MappedTest, AssertsCompiledSQL): Column("person_id", Integer, ForeignKey("people.person_id")), ) + @classmethod + def setup_classes(cls): + cls.classes["Engineer"] = Engineer + cls.classes["Person"] = Person + cls.classes["Manager"] = Manager + cls.classes["Machine"] = Machine + cls.classes["Boss"] = Boss + cls.classes["Company"] = Company + cls.classes["Paperwork"] = Paperwork + @classmethod def insert_data(cls, connection): diff --git a/test/orm/inheritance/test_polymorphic_rel.py b/test/orm/inheritance/test_polymorphic_rel.py index 8be25f2b9d..db36e36b0d 100644 --- a/test/orm/inheritance/test_polymorphic_rel.py +++ b/test/orm/inheritance/test_polymorphic_rel.py @@ -1365,6 +1365,55 @@ class _PolymorphicTestBase(object): expected, ) + def _join_to_poly_wp_one(self, sess): + wp = with_polymorphic(self.classes.Person, "*") + return ( + sess.query(wp.name, self.classes.Company.name) + .join(self.classes.Company.employees.of_type(wp)) + .order_by(wp.person_id) + ) + + def _join_to_poly_wp_two(self, sess): + wp = with_polymorphic(self.classes.Person, "*", aliased=True) + return ( + sess.query(wp.name, self.classes.Company.name) + .join(self.classes.Company.employees.of_type(wp)) + .order_by(wp.person_id) + ) + + def _join_to_poly_wp_three(self, sess): + wp = with_polymorphic( + self.classes.Person, "*", aliased=True, flat=True + ) + return ( + sess.query(wp.name, self.classes.Company.name) + .join(self.classes.Company.employees.of_type(wp)) + .order_by(wp.person_id) + ) + + @testing.combinations( + lambda self, sess: ( + sess.query(self.classes.Person.name, self.classes.Company.name) + .join(self.classes.Company.employees) + .order_by(self.classes.Person.person_id) + ), + _join_to_poly_wp_one, + _join_to_poly_wp_two, + _join_to_poly_wp_three, + ) + def test_mixed_entities_join_to_poly(self, q): + sess = create_session() + expected = [ + ("dilbert", "MegaCorp, Inc."), + ("wally", "MegaCorp, Inc."), + ("pointy haired boss", "MegaCorp, Inc."), + ("dogbert", "MegaCorp, Inc."), + ("vlad", "Elbonia, Inc."), + ] + eq_( + q(self, sess).all(), expected, + ) + def test_mixed_entities_two(self): sess = create_session() expected = [