]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Key subqueryloaders on the property object, not string key
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 3 Apr 2020 20:00:22 +0000 (16:00 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 3 Apr 2020 20:00:22 +0000 (16:00 -0400)
Fixed bug in :func:`.orm.selectinload` loading option where two or more
loaders that represent different relationships with the same string key
name as referenced from a single :func:`.orm.with_polymorphic` construct
with multiple subclass mappers would fail to invoke each subqueryload
separately, instead making use of a single string-based slot that would
prevent the other loaders from being invoked.

Fixes: #5228
Change-Id: Id0d1db8029ca88c13c0068115fe673adb7a68407

doc/build/changelog/unreleased_13/5228.rst [new file with mode: 0644]
lib/sqlalchemy/orm/strategies.py
test/orm/test_selectin_relations.py

diff --git a/doc/build/changelog/unreleased_13/5228.rst b/doc/build/changelog/unreleased_13/5228.rst
new file mode 100644 (file)
index 0000000..2f0b6a5
--- /dev/null
@@ -0,0 +1,11 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 5228
+
+    Fixed bug in :func:`.orm.selectinload` loading option where two or more
+    loaders that represent different relationships with the same string key
+    name as referenced from a single :func:`.orm.with_polymorphic` construct
+    with multiple subclass mappers would fail to invoke each subqueryload
+    separately, instead making use of a single string-based slot that would
+    prevent the other loaders from being invoked.
+
index a55295fc77e9b2f599a7b14064309b59dc16cf9b..6967412161412795c8a4f9f14a32e41f977632da 100644 (file)
@@ -2291,7 +2291,9 @@ class SelectInLoader(PostLoader, util.MemoizedSlots):
         if not orm_util._entity_isa(path[-1], self.parent):
             return
 
-        if loading.PostLoad.path_exists(context, selectin_path, self.key):
+        if loading.PostLoad.path_exists(
+            context, selectin_path, self.parent_property
+        ):
             return
 
         path_w_prop = path[self.parent_property]
@@ -2319,7 +2321,7 @@ class SelectInLoader(PostLoader, util.MemoizedSlots):
             context,
             selectin_path,
             self.parent,
-            self.key,
+            self.parent_property,
             self._load_for_path,
             effective_entity,
         )
index c7bac95c99fa6d060653c7ee05e4f3947fe59361..5039ab0a8ac0d25f534d9d9880479c28c724f8ef 100644 (file)
@@ -28,6 +28,8 @@ from sqlalchemy.testing import is_
 from sqlalchemy.testing import is_not_
 from sqlalchemy.testing import is_true
 from sqlalchemy.testing import mock
+from sqlalchemy.testing.assertsql import AllOf
+from sqlalchemy.testing.assertsql import assert_engine
 from sqlalchemy.testing.assertsql import CompiledSQL
 from sqlalchemy.testing.schema import Column
 from sqlalchemy.testing.schema import Table
@@ -3312,3 +3314,110 @@ class M2OWDegradeTest(
                 A(id=5, b=b1),
             ],
         )
+
+
+class SameNamePolymorphicTest(fixtures.DeclarativeMappedTest):
+    @classmethod
+    def setup_classes(cls):
+        Base = cls.DeclarativeBasic
+
+        class GenericParent(Base):
+            __tablename__ = "generic_parent"
+            id = Column(Integer, primary_key=True)
+            type = Column(String(50), nullable=False)
+
+            __mapper_args__ = {
+                "polymorphic_on": type,
+                "polymorphic_identity": "generic_parent",
+            }
+
+        class ParentA(GenericParent):
+            __tablename__ = "parent_a"
+
+            id = Column(
+                Integer, ForeignKey("generic_parent.id"), primary_key=True
+            )
+            children = relationship("ChildA", back_populates="parent")
+
+            __mapper_args__ = {"polymorphic_identity": "parent_a"}
+
+        class ParentB(GenericParent):
+            __tablename__ = "parent_b"
+
+            id = Column(
+                Integer, ForeignKey("generic_parent.id"), primary_key=True
+            )
+            children = relationship("ChildB", back_populates="parent")
+
+            __mapper_args__ = {"polymorphic_identity": "parent_b"}
+
+        class ChildA(Base):
+            __tablename__ = "child_a"
+            id = Column(Integer, primary_key=True)
+            parent_id = Column(
+                Integer, ForeignKey("parent_a.id"), nullable=False
+            )
+            parent = relationship("ParentA", back_populates="children")
+
+        class ChildB(Base):
+            __tablename__ = "child_b"
+
+            id = Column(Integer, primary_key=True)
+            parent_id = Column(
+                Integer, ForeignKey("parent_b.id"), nullable=False
+            )
+            parent = relationship("ParentB", back_populates="children")
+
+    @classmethod
+    def insert_data(cls):
+        ParentA, ParentB, ChildA, ChildB = cls.classes(
+            "ParentA", "ParentB", "ChildA", "ChildB"
+        )
+        session = Session()
+        parent_a = ParentA(id=1)
+        parent_b = ParentB(id=2)
+        for i in range(10):
+            parent_a.children.append(ChildA())
+            parent_b.children.append(ChildB())
+        session.add_all([parent_a, parent_b])
+
+        session.commit()
+
+    def test_load_both_wpoly(self):
+        GenericParent, ParentA, ParentB, ChildA, ChildB = self.classes(
+            "GenericParent", "ParentA", "ParentB", "ChildA", "ChildB"
+        )
+        session = Session()
+
+        parent_types = with_polymorphic(GenericParent, [ParentA, ParentB])
+
+        with assert_engine(testing.db) as asserter_:
+            session.query(parent_types).options(
+                selectinload(parent_types.ParentA.children),
+                selectinload(parent_types.ParentB.children),
+            ).all()
+
+        asserter_.assert_(
+            CompiledSQL(
+                "SELECT generic_parent.id AS generic_parent_id, "
+                "generic_parent.type AS generic_parent_type, "
+                "parent_a.id AS parent_a_id, parent_b.id AS parent_b_id "
+                "FROM generic_parent LEFT OUTER JOIN parent_a "
+                "ON generic_parent.id = parent_a.id LEFT OUTER JOIN parent_b "
+                "ON generic_parent.id = parent_b.id"
+            ),
+            AllOf(
+                CompiledSQL(
+                    "SELECT child_a.parent_id AS child_a_parent_id, "
+                    "child_a.id AS child_a_id FROM child_a "
+                    "WHERE child_a.parent_id IN ([POSTCOMPILE_primary_keys])",
+                    [{"primary_keys": [1]}],
+                ),
+                CompiledSQL(
+                    "SELECT child_b.parent_id AS child_b_parent_id, "
+                    "child_b.id AS child_b_id FROM child_b "
+                    "WHERE child_b.parent_id IN ([POSTCOMPILE_primary_keys])",
+                    [{"primary_keys": [2]}],
+                ),
+            ),
+        )