--- /dev/null
+.. 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.
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
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
_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
from __future__ import annotations
+import typing
+
import sqlalchemy
from sqlalchemy import orm
import sqlalchemy.orm
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",
+ )