diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-09-26 14:38:44 -0400 |
---|---|---|
committer | mike bayer <mike_mp@zzzcomputing.com> | 2022-10-06 00:36:25 +0000 |
commit | 276349200c486eee108471b888acfc47ea19201b (patch) | |
tree | 7441fa3219f21b18c6e532bd85b25c2bbdae86f8 /lib/sqlalchemy/orm/base.py | |
parent | 566cccc8645be99a23811c39d43481d7248628b0 (diff) | |
download | sqlalchemy-276349200c486eee108471b888acfc47ea19201b.tar.gz |
implement write-only colletions, typing for dynamic
For 2.0, we provide a truly "larger than memory collection"
implementation, a write-only collection that will never
under any circumstances implicitly load the entire
collection, even during flush.
This is essentially a much more "strict" version
of the "dynamic" loader, which in fact has a lot of
scenarios that it loads the full backing collection
into memory, mostly defeating its purpose.
Typing constructs are added that support
both the new feature WriteOnlyMapping as well as the
legacy feature DynamicMapping. These have been
integrated with "annotion based mapping" so that
relationship() uses these annotations to configure
the loader strategy as well.
additional changes:
* the docs triggered a conflict in hybrid's
"transformers" section, this section is hard-coded
to Query using a pattern that doesnt seem to have
any use and isn't part of the current select()
interface, so just removed this section
* As the docs for WriteOnlyMapping are very long,
collections.rst is broken up into two pages now.
Fixes: #6229
Fixes: #7123
Change-Id: I6929f3da6e441cad92285e7309030a9bac4e429d
Diffstat (limited to 'lib/sqlalchemy/orm/base.py')
-rw-r--r-- | lib/sqlalchemy/orm/base.py | 108 |
1 files changed, 107 insertions, 1 deletions
diff --git a/lib/sqlalchemy/orm/base.py b/lib/sqlalchemy/orm/base.py index d3814abd5..20a683d8c 100644 --- a/lib/sqlalchemy/orm/base.py +++ b/lib/sqlalchemy/orm/base.py @@ -42,11 +42,13 @@ if typing.TYPE_CHECKING: from ._typing import _ExternalEntityType from ._typing import _InternalEntityType from .attributes import InstrumentedAttribute + from .dynamic import AppenderQuery from .instrumentation import ClassManager from .interfaces import PropComparator from .mapper import Mapper from .state import InstanceState from .util import AliasedClass + from .writeonly import WriteOnlyCollection from ..sql._typing import _ColumnExpressionArgument from ..sql._typing import _InfoType from ..sql.elements import ColumnElement @@ -726,7 +728,23 @@ class ORMDescriptor(Generic[_T], TypingOnly): ... -class Mapped(ORMDescriptor[_T], roles.TypedColumnsClauseRole[_T], TypingOnly): +class _MappedAnnotationBase(Generic[_T], TypingOnly): + """common class for Mapped and similar ORM container classes. + + these are classes that can appear on the left side of an ORM declarative + mapping, containing a mapped class or in some cases a collection + surrounding a mapped class. + + """ + + __slots__ = () + + +class Mapped( + ORMDescriptor[_T], + roles.TypedColumnsClauseRole[_T], + _MappedAnnotationBase[_T], +): """Represent an ORM mapped attribute on a mapped class. This class represents the complete descriptor interface for any class @@ -811,3 +829,91 @@ class _DeclarativeMapped(Mapped[_T], _MappedAttribute[_T]): """ __slots__ = () + + +class DynamicMapped(_MappedAnnotationBase[_T]): + """Represent the ORM mapped attribute type for a "dynamic" relationship. + + The :class:`_orm.DynamicMapped` type annotation may be used in an + :ref:`Annotated Declarative Table <orm_declarative_mapped_column>` mapping + to indicate that the ``lazy="dynamic"`` loader strategy should be used + for a particular :func:`_orm.relationship`. + + .. legacy:: The "dynamic" lazy loader strategy is the legacy form of what + is now the "write_only" strategy described in the section + :ref:`write_only_relationship`. + + E.g.:: + + class User(Base): + __tablename__ = "user" + id: Mapped[int] = mapped_column(primary_key=True) + addresses: DynamicMapped[Address] = relationship( + cascade="all,delete-orphan" + ) + + See the section :ref:`dynamic_relationship` for background. + + .. versionadded:: 2.0 + + .. seealso:: + + :ref:`dynamic_relationship` - complete background + + :class:`.WriteOnlyMapped` - fully 2.0 style version + + """ + + __slots__ = () + + if TYPE_CHECKING: + + def __get__( + self, instance: Optional[object], owner: Any + ) -> AppenderQuery[_T]: + ... + + def __set__(self, instance: Any, value: typing.Collection[_T]) -> None: + ... + + +class WriteOnlyMapped(_MappedAnnotationBase[_T]): + """Represent the ORM mapped attribute type for a "write only" relationship. + + The :class:`_orm.WriteOnlyMapped` type annotation may be used in an + :ref:`Annotated Declarative Table <orm_declarative_mapped_column>` mapping + to indicate that the ``lazy="write_only"`` loader strategy should be used + for a particular :func:`_orm.relationship`. + + E.g.:: + + class User(Base): + __tablename__ = "user" + id: Mapped[int] = mapped_column(primary_key=True) + addresses: WriteOnlyMapped[Address] = relationship( + cascade="all,delete-orphan" + ) + + See the section :ref:`write_only_relationship` for background. + + .. versionadded:: 2.0 + + .. seealso:: + + :ref:`write_only_relationship` - complete background + + :class:`.DynamicMapped` - includes legacy :class:`_orm.Query` support + + """ + + __slots__ = () + + if TYPE_CHECKING: + + def __get__( + self, instance: Optional[object], owner: Any + ) -> WriteOnlyCollection[_T]: + ... + + def __set__(self, instance: Any, value: typing.Collection[_T]) -> None: + ... |