]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
only consider column / relationship attrs for subclass IN
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 28 Apr 2024 17:39:08 +0000 (13:39 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 28 Apr 2024 23:43:05 +0000 (19:43 -0400)
Fixed issue in  :func:`_orm.selectin_polymorhpic` loader option where
attributes defined with :func:`_orm.composite` on a superclass would cause
an internal exception on load.

Define the prop for :class:`.PropRegistry` as a
:class:`.StrategizedProperty`; we dont make path registries for
descriptor props like synonyms, composites, etc.

Fixes: #11291
Change-Id: I6f16844d2483dc86ab402b0b8b1f09561498aa1f

doc/build/changelog/unreleased_20/11291.rst [new file with mode: 0644]
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/path_registry.py
test/orm/inheritance/test_poly_loading.py

diff --git a/doc/build/changelog/unreleased_20/11291.rst b/doc/build/changelog/unreleased_20/11291.rst
new file mode 100644 (file)
index 0000000..e341ff8
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 11291
+
+    Fixed issue in  :func:`_orm.selectin_polymorhpic` loader option where
+    attributes defined with :func:`_orm.composite` on a superclass would cause
+    an internal exception on load.
+
index 9e1be2fbba5f3f3dd1902149589b3b137e7a88cc..b8f2a5a84d4790eb3468f7402e44a50dd473da28 100644 (file)
@@ -3831,7 +3831,7 @@ class Mapper(
             classes_to_include.add(m)
             m = m.inherits
 
-        for prop in self.attrs:
+        for prop in self.column_attrs + self.relationships:
             # skip prop keys that are not instrumented on the mapped class.
             # this is primarily the "_sa_polymorphic_on" property that gets
             # created for an ad-hoc polymorphic_on SQL expression, issue #8704
index 76484b3e68fd66e7f4c6ee79aae3164f590bbf72..4ee8ac71b84d6b872cfc317e4d844cdd409bd965 100644 (file)
@@ -35,7 +35,7 @@ from ..sql.cache_key import HasCacheKey
 
 if TYPE_CHECKING:
     from ._typing import _InternalEntityType
-    from .interfaces import MapperProperty
+    from .interfaces import StrategizedProperty
     from .mapper import Mapper
     from .relationships import RelationshipProperty
     from .util import AliasedInsp
@@ -57,13 +57,13 @@ else:
 _SerializedPath = List[Any]
 _StrPathToken = str
 _PathElementType = Union[
-    _StrPathToken, "_InternalEntityType[Any]", "MapperProperty[Any]"
+    _StrPathToken, "_InternalEntityType[Any]", "StrategizedProperty[Any]"
 ]
 
 # the representation is in fact
 # a tuple with alternating:
-# [_InternalEntityType[Any], Union[str, MapperProperty[Any]],
-# _InternalEntityType[Any], Union[str, MapperProperty[Any]], ...]
+# [_InternalEntityType[Any], Union[str, StrategizedProperty[Any]],
+# _InternalEntityType[Any], Union[str, StrategizedProperty[Any]], ...]
 # this might someday be a tuple of 2-tuples instead, but paths can be
 # chopped at odd intervals as well so this is less flexible
 _PathRepresentation = Tuple[_PathElementType, ...]
@@ -71,7 +71,7 @@ _PathRepresentation = Tuple[_PathElementType, ...]
 # NOTE: these names are weird since the array is 0-indexed,
 # the "_Odd" entries are at 0, 2, 4, etc
 _OddPathRepresentation = Sequence["_InternalEntityType[Any]"]
-_EvenPathRepresentation = Sequence[Union["MapperProperty[Any]", str]]
+_EvenPathRepresentation = Sequence[Union["StrategizedProperty[Any]", str]]
 
 
 log = logging.getLogger(__name__)
@@ -197,7 +197,9 @@ class PathRegistry(HasCacheKey):
     ) -> AbstractEntityRegistry: ...
 
     @overload
-    def __getitem__(self, entity: MapperProperty[Any]) -> PropRegistry: ...
+    def __getitem__(
+        self, entity: StrategizedProperty[Any]
+    ) -> PropRegistry: ...
 
     def __getitem__(
         self,
@@ -206,7 +208,7 @@ class PathRegistry(HasCacheKey):
             int,
             slice,
             _InternalEntityType[Any],
-            MapperProperty[Any],
+            StrategizedProperty[Any],
         ],
     ) -> Union[
         TokenRegistry,
@@ -225,7 +227,7 @@ class PathRegistry(HasCacheKey):
     def pairs(
         self,
     ) -> Iterator[
-        Tuple[_InternalEntityType[Any], Union[str, MapperProperty[Any]]]
+        Tuple[_InternalEntityType[Any], Union[str, StrategizedProperty[Any]]]
     ]:
         odd_path = cast(_OddPathRepresentation, self.path)
         even_path = cast(_EvenPathRepresentation, odd_path)
@@ -531,15 +533,16 @@ class PropRegistry(PathRegistry):
     inherit_cache = True
     is_property = True
 
-    prop: MapperProperty[Any]
+    prop: StrategizedProperty[Any]
     mapper: Optional[Mapper[Any]]
     entity: Optional[_InternalEntityType[Any]]
 
     def __init__(
-        self, parent: AbstractEntityRegistry, prop: MapperProperty[Any]
+        self, parent: AbstractEntityRegistry, prop: StrategizedProperty[Any]
     ):
+
         # restate this path in terms of the
-        # given MapperProperty's parent.
+        # given StrategizedProperty's parent.
         insp = cast("_InternalEntityType[Any]", parent[-1])
         natural_parent: AbstractEntityRegistry = parent
 
index a768c32754af24f74efd1f369c87b8be4eeb21cf..58cf7b54271f302040558f5f8919ee918132476a 100644 (file)
@@ -1470,18 +1470,10 @@ class NoBaseWPPlusAliasedTest(
 
 
 class CompositeAttributesTest(fixtures.TestBase):
-    @testing.fixture
-    def mapping_fixture(self, registry, connection):
-        Base = registry.generate_base()
 
-        class BaseCls(Base):
-            __tablename__ = "base"
-            id = Column(
-                Integer, primary_key=True, test_needs_autoincrement=True
-            )
-            type = Column(String(50))
-
-            __mapper_args__ = {"polymorphic_on": type}
+    @testing.fixture(params=("base", "sub"))
+    def mapping_fixture(self, request, registry, connection):
+        Base = registry.generate_base()
 
         class XYThing:
             def __init__(self, x, y):
@@ -1501,13 +1493,28 @@ class CompositeAttributesTest(fixtures.TestBase):
             def __ne__(self, other):
                 return not self.__eq__(other)
 
+        class BaseCls(Base):
+            __tablename__ = "base"
+            id = Column(
+                Integer, primary_key=True, test_needs_autoincrement=True
+            )
+            type = Column(String(50))
+
+            if request.param == "base":
+                comp1 = composite(
+                    XYThing, Column("x1", Integer), Column("y1", Integer)
+                )
+
+            __mapper_args__ = {"polymorphic_on": type}
+
         class A(ComparableEntity, BaseCls):
             __tablename__ = "a"
             id = Column(ForeignKey(BaseCls.id), primary_key=True)
             thing1 = Column(String(50))
-            comp1 = composite(
-                XYThing, Column("x1", Integer), Column("y1", Integer)
-            )
+            if request.param == "sub":
+                comp1 = composite(
+                    XYThing, Column("x1", Integer), Column("y1", Integer)
+                )
 
             __mapper_args__ = {
                 "polymorphic_identity": "a",