]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
include WriteOnlyMapped, DynamicMapped in abs import lookup
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 3 Oct 2023 17:42:35 +0000 (13:42 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 3 Oct 2023 17:43:52 +0000 (13:43 -0400)
Fixed issue where :class:`.Mapped` symbols like :class:`.WriteOnlyMapped`
and :class:`.DynamicMapped` could not be correctly resolved when referenced
as an element of a sub-module in the given annotation, assuming
string-based or "future annotations" style annotations.

Fixes: #10412
Change-Id: I188146a6de7f6f80ec0ebf6e982b7842a78adc54

doc/build/changelog/unreleased_20/10412.rst [new file with mode: 0644]
lib/sqlalchemy/orm/util.py
test/orm/declarative/test_abs_import_only.py

diff --git a/doc/build/changelog/unreleased_20/10412.rst b/doc/build/changelog/unreleased_20/10412.rst
new file mode 100644 (file)
index 0000000..d33b49b
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 10412
+
+    Fixed issue where :class:`.Mapped` symbols like :class:`.WriteOnlyMapped`
+    and :class:`.DynamicMapped` could not be correctly resolved when referenced
+    as an element of a sub-module in the given annotation, assuming
+    string-based or "future annotations" style annotations.
index e9a85b9ae66539aab13f75044334ab4525538d98..573cc2c436c5d338eaa377305dcc843e9ea0c431 100644 (file)
@@ -45,6 +45,7 @@ from .base import _never_set as _never_set  # noqa: F401
 from .base import _none_set as _none_set  # noqa: F401
 from .base import attribute_str as attribute_str  # noqa: F401
 from .base import class_mapper as class_mapper
+from .base import DynamicMapped
 from .base import InspectionAttr as InspectionAttr
 from .base import instance_str as instance_str  # noqa: F401
 from .base import Mapped
@@ -55,6 +56,7 @@ from .base import ORMDescriptor
 from .base import state_attribute_str as state_attribute_str  # noqa: F401
 from .base import state_class_str as state_class_str  # noqa: F401
 from .base import state_str as state_str  # noqa: F401
+from .base import WriteOnlyMapped
 from .interfaces import CriteriaOption
 from .interfaces import MapperProperty as MapperProperty
 from .interfaces import ORMColumnsClauseRole
@@ -138,7 +140,14 @@ all_cascades = frozenset(
 
 
 _de_stringify_partial = functools.partial(
-    functools.partial, locals_=util.immutabledict({"Mapped": Mapped})
+    functools.partial,
+    locals_=util.immutabledict(
+        {
+            "Mapped": Mapped,
+            "WriteOnlyMapped": WriteOnlyMapped,
+            "DynamicMapped": DynamicMapped,
+        }
+    ),
 )
 
 # partial is practically useless as we have to write out the whole
index e700b4cc2d2947d4b85a7cc2bc2397702fa0a559..e1447364e6676c3d7896ef5ebdfeccd4307f8793 100644 (file)
@@ -6,6 +6,8 @@ mappings while guaranteeing that the Mapped name is not locally present
 
 from __future__ import annotations
 
+import typing
+
 import sqlalchemy
 from sqlalchemy import orm
 import sqlalchemy.orm
@@ -45,3 +47,42 @@ class MappedColumnTest(
             sqlalchemy.select(Foo),
             "SELECT foo.id, foo.data, foo.data2, foo.data3 FROM foo",
         )
+
+    @sqlalchemy.testing.variation(
+        "construct", ["Mapped", "WriteOnlyMapped", "DynamicMapped"]
+    )
+    def test_fully_qualified_writeonly_mapped_name(self, decl_base, construct):
+        """futher variation in issue #10412"""
+
+        class Foo(decl_base):
+            __tablename__ = "foo"
+
+            id: sqlalchemy.orm.Mapped[int] = sqlalchemy.orm.mapped_column(
+                primary_key=True
+            )
+
+            if construct.Mapped:
+                bars: orm.Mapped[typing.List[Bar]] = orm.relationship()
+            elif construct.WriteOnlyMapped:
+                bars: orm.WriteOnlyMapped[
+                    typing.List[Bar]
+                ] = orm.relationship()
+            elif construct.DynamicMapped:
+                bars: orm.DynamicMapped[typing.List[Bar]] = orm.relationship()
+            else:
+                construct.fail()
+
+        class Bar(decl_base):
+            __tablename__ = "bar"
+
+            id: sqlalchemy.orm.Mapped[int] = sqlalchemy.orm.mapped_column(
+                primary_key=True
+            )
+            foo_id: sqlalchemy.orm.Mapped[int] = sqlalchemy.orm.mapped_column(
+                sqlalchemy.ForeignKey("foo.id")
+            )
+
+        self.assert_compile(
+            sqlalchemy.select(Foo).join(Foo.bars),
+            "SELECT foo.id FROM foo JOIN bar ON foo.id = bar.foo_id",
+        )