]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Alias the onclause if ORM join is to same polymorphic selectable
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 27 Apr 2020 20:51:43 +0000 (16:51 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 27 Apr 2020 22:38:49 +0000 (18:38 -0400)
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

doc/build/changelog/unreleased_13/5288.rst [new file with mode: 0644]
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/relationships.py
test/orm/inheritance/_poly_fixtures.py
test/orm/inheritance/test_polymorphic_rel.py

diff --git a/doc/build/changelog/unreleased_13/5288.rst b/doc/build/changelog/unreleased_13/5288.rst
new file mode 100644 (file)
index 0000000..d5fbdf5
--- /dev/null
@@ -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.
index ab49a4dccc0ac2aafe893831684dfa26d91d8454..98e997078663e05f5f11207dad396bf3e0e07d44 100644 (file)
@@ -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
                 ):
index 6ac56a324a81542a2b7f65983ae3d542d393f9c4..85f23d15986f52bcbffba0aba04f32bcd7ef39ba 100644 (file)
@@ -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
index c9cde7a81cd1d5f3fba5d9f13ce8ece61447ae71..5d23e7801cd7d8fb83a898dffa9550be28af93fa 100644 (file)
@@ -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):
 
index 8be25f2b9d3034a53c363f266a572bd265509eea..db36e36b0d2289e5841d77bcbaabde01523348b1 100644 (file)
@@ -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 = [