1# orm/base.py
2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8"""Constants and rudimental functions used throughout the ORM.
9
10"""
11
12from __future__ import annotations
13
14from enum import Enum
15import operator
16import typing
17from typing import Any
18from typing import Callable
19from typing import Dict
20from typing import Generic
21from typing import no_type_check
22from typing import Optional
23from typing import overload
24from typing import Tuple
25from typing import Type
26from typing import TYPE_CHECKING
27from typing import TypeVar
28from typing import Union
29
30from . import exc
31from ._typing import insp_is_mapper
32from .. import exc as sa_exc
33from .. import inspection
34from .. import util
35from ..sql import roles
36from ..sql.elements import SQLColumnExpression
37from ..sql.elements import SQLCoreOperations
38from ..util import FastIntFlag
39from ..util.langhelpers import TypingOnly
40from ..util.typing import Literal
41
42if typing.TYPE_CHECKING:
43 from ._typing import _EntityType
44 from ._typing import _ExternalEntityType
45 from ._typing import _InternalEntityType
46 from .attributes import InstrumentedAttribute
47 from .dynamic import AppenderQuery
48 from .instrumentation import ClassManager
49 from .interfaces import PropComparator
50 from .mapper import Mapper
51 from .state import InstanceState
52 from .util import AliasedClass
53 from .writeonly import WriteOnlyCollection
54 from ..sql._typing import _ColumnExpressionArgument
55 from ..sql._typing import _InfoType
56 from ..sql.elements import ColumnElement
57 from ..sql.operators import OperatorType
58
59_T = TypeVar("_T", bound=Any)
60_T_co = TypeVar("_T_co", bound=Any, covariant=True)
61
62_O = TypeVar("_O", bound=object)
63
64
65class LoaderCallableStatus(Enum):
66 PASSIVE_NO_RESULT = 0
67 """Symbol returned by a loader callable or other attribute/history
68 retrieval operation when a value could not be determined, based
69 on loader callable flags.
70 """
71
72 PASSIVE_CLASS_MISMATCH = 1
73 """Symbol indicating that an object is locally present for a given
74 primary key identity but it is not of the requested class. The
75 return value is therefore None and no SQL should be emitted."""
76
77 ATTR_WAS_SET = 2
78 """Symbol returned by a loader callable to indicate the
79 retrieved value, or values, were assigned to their attributes
80 on the target object.
81 """
82
83 ATTR_EMPTY = 3
84 """Symbol used internally to indicate an attribute had no callable."""
85
86 NO_VALUE = 4
87 """Symbol which may be placed as the 'previous' value of an attribute,
88 indicating no value was loaded for an attribute when it was modified,
89 and flags indicated we were not to load it.
90 """
91
92 NEVER_SET = NO_VALUE
93 """
94 Synonymous with NO_VALUE
95
96 .. versionchanged:: 1.4 NEVER_SET was merged with NO_VALUE
97
98 """
99
100
101(
102 PASSIVE_NO_RESULT,
103 PASSIVE_CLASS_MISMATCH,
104 ATTR_WAS_SET,
105 ATTR_EMPTY,
106 NO_VALUE,
107) = tuple(LoaderCallableStatus)
108
109NEVER_SET = NO_VALUE
110
111
112class PassiveFlag(FastIntFlag):
113 """Bitflag interface that passes options onto loader callables"""
114
115 NO_CHANGE = 0
116 """No callables or SQL should be emitted on attribute access
117 and no state should change
118 """
119
120 CALLABLES_OK = 1
121 """Loader callables can be fired off if a value
122 is not present.
123 """
124
125 SQL_OK = 2
126 """Loader callables can emit SQL at least on scalar value attributes."""
127
128 RELATED_OBJECT_OK = 4
129 """Callables can use SQL to load related objects as well
130 as scalar value attributes.
131 """
132
133 INIT_OK = 8
134 """Attributes should be initialized with a blank
135 value (None or an empty collection) upon get, if no other
136 value can be obtained.
137 """
138
139 NON_PERSISTENT_OK = 16
140 """Callables can be emitted if the parent is not persistent."""
141
142 LOAD_AGAINST_COMMITTED = 32
143 """Callables should use committed values as primary/foreign keys during a
144 load.
145 """
146
147 NO_AUTOFLUSH = 64
148 """Loader callables should disable autoflush.""",
149
150 NO_RAISE = 128
151 """Loader callables should not raise any assertions"""
152
153 DEFERRED_HISTORY_LOAD = 256
154 """indicates special load of the previous value of an attribute"""
155
156 INCLUDE_PENDING_MUTATIONS = 512
157
158 # pre-packaged sets of flags used as inputs
159 PASSIVE_OFF = (
160 RELATED_OBJECT_OK | NON_PERSISTENT_OK | INIT_OK | CALLABLES_OK | SQL_OK
161 )
162 "Callables can be emitted in all cases."
163
164 PASSIVE_RETURN_NO_VALUE = PASSIVE_OFF ^ INIT_OK
165 """PASSIVE_OFF ^ INIT_OK"""
166
167 PASSIVE_NO_INITIALIZE = PASSIVE_RETURN_NO_VALUE ^ CALLABLES_OK
168 "PASSIVE_RETURN_NO_VALUE ^ CALLABLES_OK"
169
170 PASSIVE_NO_FETCH = PASSIVE_OFF ^ SQL_OK
171 "PASSIVE_OFF ^ SQL_OK"
172
173 PASSIVE_NO_FETCH_RELATED = PASSIVE_OFF ^ RELATED_OBJECT_OK
174 "PASSIVE_OFF ^ RELATED_OBJECT_OK"
175
176 PASSIVE_ONLY_PERSISTENT = PASSIVE_OFF ^ NON_PERSISTENT_OK
177 "PASSIVE_OFF ^ NON_PERSISTENT_OK"
178
179 PASSIVE_MERGE = PASSIVE_OFF | NO_RAISE
180 """PASSIVE_OFF | NO_RAISE
181
182 Symbol used specifically for session.merge() and similar cases
183
184 """
185
186
187(
188 NO_CHANGE,
189 CALLABLES_OK,
190 SQL_OK,
191 RELATED_OBJECT_OK,
192 INIT_OK,
193 NON_PERSISTENT_OK,
194 LOAD_AGAINST_COMMITTED,
195 NO_AUTOFLUSH,
196 NO_RAISE,
197 DEFERRED_HISTORY_LOAD,
198 INCLUDE_PENDING_MUTATIONS,
199 PASSIVE_OFF,
200 PASSIVE_RETURN_NO_VALUE,
201 PASSIVE_NO_INITIALIZE,
202 PASSIVE_NO_FETCH,
203 PASSIVE_NO_FETCH_RELATED,
204 PASSIVE_ONLY_PERSISTENT,
205 PASSIVE_MERGE,
206) = PassiveFlag.__members__.values()
207
208DEFAULT_MANAGER_ATTR = "_sa_class_manager"
209DEFAULT_STATE_ATTR = "_sa_instance_state"
210
211
212class EventConstants(Enum):
213 EXT_CONTINUE = 1
214 EXT_STOP = 2
215 EXT_SKIP = 3
216 NO_KEY = 4
217 """indicates an :class:`.AttributeEvent` event that did not have any
218 key argument.
219
220 .. versionadded:: 2.0
221
222 """
223
224
225EXT_CONTINUE, EXT_STOP, EXT_SKIP, NO_KEY = tuple(EventConstants)
226
227
228class RelationshipDirection(Enum):
229 """enumeration which indicates the 'direction' of a
230 :class:`_orm.RelationshipProperty`.
231
232 :class:`.RelationshipDirection` is accessible from the
233 :attr:`_orm.Relationship.direction` attribute of
234 :class:`_orm.RelationshipProperty`.
235
236 """
237
238 ONETOMANY = 1
239 """Indicates the one-to-many direction for a :func:`_orm.relationship`.
240
241 This symbol is typically used by the internals but may be exposed within
242 certain API features.
243
244 """
245
246 MANYTOONE = 2
247 """Indicates the many-to-one direction for a :func:`_orm.relationship`.
248
249 This symbol is typically used by the internals but may be exposed within
250 certain API features.
251
252 """
253
254 MANYTOMANY = 3
255 """Indicates the many-to-many direction for a :func:`_orm.relationship`.
256
257 This symbol is typically used by the internals but may be exposed within
258 certain API features.
259
260 """
261
262
263ONETOMANY, MANYTOONE, MANYTOMANY = tuple(RelationshipDirection)
264
265
266class InspectionAttrExtensionType(Enum):
267 """Symbols indicating the type of extension that a
268 :class:`.InspectionAttr` is part of."""
269
270
271class NotExtension(InspectionAttrExtensionType):
272 NOT_EXTENSION = "not_extension"
273 """Symbol indicating an :class:`InspectionAttr` that's
274 not part of sqlalchemy.ext.
275
276 Is assigned to the :attr:`.InspectionAttr.extension_type`
277 attribute.
278
279 """
280
281
282_never_set = frozenset([NEVER_SET])
283
284_none_set = frozenset([None, NEVER_SET, PASSIVE_NO_RESULT])
285
286_SET_DEFERRED_EXPIRED = util.symbol("SET_DEFERRED_EXPIRED")
287
288_DEFER_FOR_STATE = util.symbol("DEFER_FOR_STATE")
289
290_RAISE_FOR_STATE = util.symbol("RAISE_FOR_STATE")
291
292
293_F = TypeVar("_F", bound=Callable[..., Any])
294_Self = TypeVar("_Self")
295
296
297def _assertions(
298 *assertions: Any,
299) -> Callable[[_F], _F]:
300 @util.decorator
301 def generate(fn: _F, self: _Self, *args: Any, **kw: Any) -> _Self:
302 for assertion in assertions:
303 assertion(self, fn.__name__)
304 fn(self, *args, **kw)
305 return self
306
307 return generate
308
309
310if TYPE_CHECKING:
311
312 def manager_of_class(cls: Type[_O]) -> ClassManager[_O]: ...
313
314 @overload
315 def opt_manager_of_class(cls: AliasedClass[Any]) -> None: ...
316
317 @overload
318 def opt_manager_of_class(
319 cls: _ExternalEntityType[_O],
320 ) -> Optional[ClassManager[_O]]: ...
321
322 def opt_manager_of_class(
323 cls: _ExternalEntityType[_O],
324 ) -> Optional[ClassManager[_O]]: ...
325
326 def instance_state(instance: _O) -> InstanceState[_O]: ...
327
328 def instance_dict(instance: object) -> Dict[str, Any]: ...
329
330else:
331 # these can be replaced by sqlalchemy.ext.instrumentation
332 # if augmented class instrumentation is enabled.
333
334 def manager_of_class(cls):
335 try:
336 return cls.__dict__[DEFAULT_MANAGER_ATTR]
337 except KeyError as ke:
338 raise exc.UnmappedClassError(
339 cls, f"Can't locate an instrumentation manager for class {cls}"
340 ) from ke
341
342 def opt_manager_of_class(cls):
343 return cls.__dict__.get(DEFAULT_MANAGER_ATTR)
344
345 instance_state = operator.attrgetter(DEFAULT_STATE_ATTR)
346
347 instance_dict = operator.attrgetter("__dict__")
348
349
350def instance_str(instance: object) -> str:
351 """Return a string describing an instance."""
352
353 return state_str(instance_state(instance))
354
355
356def state_str(state: InstanceState[Any]) -> str:
357 """Return a string describing an instance via its InstanceState."""
358
359 if state is None:
360 return "None"
361 else:
362 return "<%s at 0x%x>" % (state.class_.__name__, id(state.obj()))
363
364
365def state_class_str(state: InstanceState[Any]) -> str:
366 """Return a string describing an instance's class via its
367 InstanceState.
368 """
369
370 if state is None:
371 return "None"
372 else:
373 return "<%s>" % (state.class_.__name__,)
374
375
376def attribute_str(instance: object, attribute: str) -> str:
377 return instance_str(instance) + "." + attribute
378
379
380def state_attribute_str(state: InstanceState[Any], attribute: str) -> str:
381 return state_str(state) + "." + attribute
382
383
384def object_mapper(instance: _T) -> Mapper[_T]:
385 """Given an object, return the primary Mapper associated with the object
386 instance.
387
388 Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError`
389 if no mapping is configured.
390
391 This function is available via the inspection system as::
392
393 inspect(instance).mapper
394
395 Using the inspection system will raise
396 :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is
397 not part of a mapping.
398
399 """
400 return object_state(instance).mapper
401
402
403def object_state(instance: _T) -> InstanceState[_T]:
404 """Given an object, return the :class:`.InstanceState`
405 associated with the object.
406
407 Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError`
408 if no mapping is configured.
409
410 Equivalent functionality is available via the :func:`_sa.inspect`
411 function as::
412
413 inspect(instance)
414
415 Using the inspection system will raise
416 :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is
417 not part of a mapping.
418
419 """
420 state = _inspect_mapped_object(instance)
421 if state is None:
422 raise exc.UnmappedInstanceError(instance)
423 else:
424 return state
425
426
427@inspection._inspects(object)
428def _inspect_mapped_object(instance: _T) -> Optional[InstanceState[_T]]:
429 try:
430 return instance_state(instance)
431 except (exc.UnmappedClassError,) + exc.NO_STATE:
432 return None
433
434
435def _class_to_mapper(
436 class_or_mapper: Union[Mapper[_T], Type[_T]]
437) -> Mapper[_T]:
438 # can't get mypy to see an overload for this
439 insp = inspection.inspect(class_or_mapper, False)
440 if insp is not None:
441 return insp.mapper # type: ignore
442 else:
443 assert isinstance(class_or_mapper, type)
444 raise exc.UnmappedClassError(class_or_mapper)
445
446
447def _mapper_or_none(
448 entity: Union[Type[_T], _InternalEntityType[_T]]
449) -> Optional[Mapper[_T]]:
450 """Return the :class:`_orm.Mapper` for the given class or None if the
451 class is not mapped.
452 """
453
454 # can't get mypy to see an overload for this
455 insp = inspection.inspect(entity, False)
456 if insp is not None:
457 return insp.mapper # type: ignore
458 else:
459 return None
460
461
462def _is_mapped_class(entity: Any) -> bool:
463 """Return True if the given object is a mapped class,
464 :class:`_orm.Mapper`, or :class:`.AliasedClass`.
465 """
466
467 insp = inspection.inspect(entity, False)
468 return (
469 insp is not None
470 and not insp.is_clause_element
471 and (insp.is_mapper or insp.is_aliased_class)
472 )
473
474
475def _is_aliased_class(entity: Any) -> bool:
476 insp = inspection.inspect(entity, False)
477 return insp is not None and getattr(insp, "is_aliased_class", False)
478
479
480@no_type_check
481def _entity_descriptor(entity: _EntityType[Any], key: str) -> Any:
482 """Return a class attribute given an entity and string name.
483
484 May return :class:`.InstrumentedAttribute` or user-defined
485 attribute.
486
487 """
488 insp = inspection.inspect(entity)
489 if insp.is_selectable:
490 description = entity
491 entity = insp.c
492 elif insp.is_aliased_class:
493 entity = insp.entity
494 description = entity
495 elif hasattr(insp, "mapper"):
496 description = entity = insp.mapper.class_
497 else:
498 description = entity
499
500 try:
501 return getattr(entity, key)
502 except AttributeError as err:
503 raise sa_exc.InvalidRequestError(
504 "Entity '%s' has no property '%s'" % (description, key)
505 ) from err
506
507
508if TYPE_CHECKING:
509
510 def _state_mapper(state: InstanceState[_O]) -> Mapper[_O]: ...
511
512else:
513 _state_mapper = util.dottedgetter("manager.mapper")
514
515
516def _inspect_mapped_class(
517 class_: Type[_O], configure: bool = False
518) -> Optional[Mapper[_O]]:
519 try:
520 class_manager = opt_manager_of_class(class_)
521 if class_manager is None or not class_manager.is_mapped:
522 return None
523 mapper = class_manager.mapper
524 except exc.NO_STATE:
525 return None
526 else:
527 if configure:
528 mapper._check_configure()
529 return mapper
530
531
532def _parse_mapper_argument(arg: Union[Mapper[_O], Type[_O]]) -> Mapper[_O]:
533 insp = inspection.inspect(arg, raiseerr=False)
534 if insp_is_mapper(insp):
535 return insp
536
537 raise sa_exc.ArgumentError(f"Mapper or mapped class expected, got {arg!r}")
538
539
540def class_mapper(class_: Type[_O], configure: bool = True) -> Mapper[_O]:
541 """Given a class, return the primary :class:`_orm.Mapper` associated
542 with the key.
543
544 Raises :exc:`.UnmappedClassError` if no mapping is configured
545 on the given class, or :exc:`.ArgumentError` if a non-class
546 object is passed.
547
548 Equivalent functionality is available via the :func:`_sa.inspect`
549 function as::
550
551 inspect(some_mapped_class)
552
553 Using the inspection system will raise
554 :class:`sqlalchemy.exc.NoInspectionAvailable` if the class is not mapped.
555
556 """
557 mapper = _inspect_mapped_class(class_, configure=configure)
558 if mapper is None:
559 if not isinstance(class_, type):
560 raise sa_exc.ArgumentError(
561 "Class object expected, got '%r'." % (class_,)
562 )
563 raise exc.UnmappedClassError(class_)
564 else:
565 return mapper
566
567
568class InspectionAttr:
569 """A base class applied to all ORM objects and attributes that are
570 related to things that can be returned by the :func:`_sa.inspect` function.
571
572 The attributes defined here allow the usage of simple boolean
573 checks to test basic facts about the object returned.
574
575 While the boolean checks here are basically the same as using
576 the Python isinstance() function, the flags here can be used without
577 the need to import all of these classes, and also such that
578 the SQLAlchemy class system can change while leaving the flags
579 here intact for forwards-compatibility.
580
581 """
582
583 __slots__: Tuple[str, ...] = ()
584
585 is_selectable = False
586 """Return True if this object is an instance of
587 :class:`_expression.Selectable`."""
588
589 is_aliased_class = False
590 """True if this object is an instance of :class:`.AliasedClass`."""
591
592 is_instance = False
593 """True if this object is an instance of :class:`.InstanceState`."""
594
595 is_mapper = False
596 """True if this object is an instance of :class:`_orm.Mapper`."""
597
598 is_bundle = False
599 """True if this object is an instance of :class:`.Bundle`."""
600
601 is_property = False
602 """True if this object is an instance of :class:`.MapperProperty`."""
603
604 is_attribute = False
605 """True if this object is a Python :term:`descriptor`.
606
607 This can refer to one of many types. Usually a
608 :class:`.QueryableAttribute` which handles attributes events on behalf
609 of a :class:`.MapperProperty`. But can also be an extension type
610 such as :class:`.AssociationProxy` or :class:`.hybrid_property`.
611 The :attr:`.InspectionAttr.extension_type` will refer to a constant
612 identifying the specific subtype.
613
614 .. seealso::
615
616 :attr:`_orm.Mapper.all_orm_descriptors`
617
618 """
619
620 _is_internal_proxy = False
621 """True if this object is an internal proxy object.
622
623 .. versionadded:: 1.2.12
624
625 """
626
627 is_clause_element = False
628 """True if this object is an instance of
629 :class:`_expression.ClauseElement`."""
630
631 extension_type: InspectionAttrExtensionType = NotExtension.NOT_EXTENSION
632 """The extension type, if any.
633 Defaults to :attr:`.interfaces.NotExtension.NOT_EXTENSION`
634
635 .. seealso::
636
637 :class:`.HybridExtensionType`
638
639 :class:`.AssociationProxyExtensionType`
640
641 """
642
643
644class InspectionAttrInfo(InspectionAttr):
645 """Adds the ``.info`` attribute to :class:`.InspectionAttr`.
646
647 The rationale for :class:`.InspectionAttr` vs. :class:`.InspectionAttrInfo`
648 is that the former is compatible as a mixin for classes that specify
649 ``__slots__``; this is essentially an implementation artifact.
650
651 """
652
653 __slots__ = ()
654
655 @util.ro_memoized_property
656 def info(self) -> _InfoType:
657 """Info dictionary associated with the object, allowing user-defined
658 data to be associated with this :class:`.InspectionAttr`.
659
660 The dictionary is generated when first accessed. Alternatively,
661 it can be specified as a constructor argument to the
662 :func:`.column_property`, :func:`_orm.relationship`, or
663 :func:`.composite`
664 functions.
665
666 .. seealso::
667
668 :attr:`.QueryableAttribute.info`
669
670 :attr:`.SchemaItem.info`
671
672 """
673 return {}
674
675
676class SQLORMOperations(SQLCoreOperations[_T_co], TypingOnly):
677 __slots__ = ()
678
679 if typing.TYPE_CHECKING:
680
681 def of_type(
682 self, class_: _EntityType[Any]
683 ) -> PropComparator[_T_co]: ...
684
685 def and_(
686 self, *criteria: _ColumnExpressionArgument[bool]
687 ) -> PropComparator[bool]: ...
688
689 def any( # noqa: A001
690 self,
691 criterion: Optional[_ColumnExpressionArgument[bool]] = None,
692 **kwargs: Any,
693 ) -> ColumnElement[bool]: ...
694
695 def has(
696 self,
697 criterion: Optional[_ColumnExpressionArgument[bool]] = None,
698 **kwargs: Any,
699 ) -> ColumnElement[bool]: ...
700
701
702class ORMDescriptor(Generic[_T_co], TypingOnly):
703 """Represent any Python descriptor that provides a SQL expression
704 construct at the class level."""
705
706 __slots__ = ()
707
708 if typing.TYPE_CHECKING:
709
710 @overload
711 def __get__(
712 self, instance: Any, owner: Literal[None]
713 ) -> ORMDescriptor[_T_co]: ...
714
715 @overload
716 def __get__(
717 self, instance: Literal[None], owner: Any
718 ) -> SQLCoreOperations[_T_co]: ...
719
720 @overload
721 def __get__(self, instance: object, owner: Any) -> _T_co: ...
722
723 def __get__(
724 self, instance: object, owner: Any
725 ) -> Union[ORMDescriptor[_T_co], SQLCoreOperations[_T_co], _T_co]: ...
726
727
728class _MappedAnnotationBase(Generic[_T_co], TypingOnly):
729 """common class for Mapped and similar ORM container classes.
730
731 these are classes that can appear on the left side of an ORM declarative
732 mapping, containing a mapped class or in some cases a collection
733 surrounding a mapped class.
734
735 """
736
737 __slots__ = ()
738
739
740class SQLORMExpression(
741 SQLORMOperations[_T_co], SQLColumnExpression[_T_co], TypingOnly
742):
743 """A type that may be used to indicate any ORM-level attribute or
744 object that acts in place of one, in the context of SQL expression
745 construction.
746
747 :class:`.SQLORMExpression` extends from the Core
748 :class:`.SQLColumnExpression` to add additional SQL methods that are ORM
749 specific, such as :meth:`.PropComparator.of_type`, and is part of the bases
750 for :class:`.InstrumentedAttribute`. It may be used in :pep:`484` typing to
751 indicate arguments or return values that should behave as ORM-level
752 attribute expressions.
753
754 .. versionadded:: 2.0.0b4
755
756
757 """
758
759 __slots__ = ()
760
761
762class Mapped(
763 SQLORMExpression[_T_co],
764 ORMDescriptor[_T_co],
765 _MappedAnnotationBase[_T_co],
766 roles.DDLConstraintColumnRole,
767):
768 """Represent an ORM mapped attribute on a mapped class.
769
770 This class represents the complete descriptor interface for any class
771 attribute that will have been :term:`instrumented` by the ORM
772 :class:`_orm.Mapper` class. Provides appropriate information to type
773 checkers such as pylance and mypy so that ORM-mapped attributes
774 are correctly typed.
775
776 The most prominent use of :class:`_orm.Mapped` is in
777 the :ref:`Declarative Mapping <orm_explicit_declarative_base>` form
778 of :class:`_orm.Mapper` configuration, where used explicitly it drives
779 the configuration of ORM attributes such as :func:`_orm.mapped_class`
780 and :func:`_orm.relationship`.
781
782 .. seealso::
783
784 :ref:`orm_explicit_declarative_base`
785
786 :ref:`orm_declarative_table`
787
788 .. tip::
789
790 The :class:`_orm.Mapped` class represents attributes that are handled
791 directly by the :class:`_orm.Mapper` class. It does not include other
792 Python descriptor classes that are provided as extensions, including
793 :ref:`hybrids_toplevel` and the :ref:`associationproxy_toplevel`.
794 While these systems still make use of ORM-specific superclasses
795 and structures, they are not :term:`instrumented` by the
796 :class:`_orm.Mapper` and instead provide their own functionality
797 when they are accessed on a class.
798
799 .. versionadded:: 1.4
800
801
802 """
803
804 __slots__ = ()
805
806 if typing.TYPE_CHECKING:
807
808 @overload
809 def __get__(
810 self, instance: None, owner: Any
811 ) -> InstrumentedAttribute[_T_co]: ...
812
813 @overload
814 def __get__(self, instance: object, owner: Any) -> _T_co: ...
815
816 def __get__(
817 self, instance: Optional[object], owner: Any
818 ) -> Union[InstrumentedAttribute[_T_co], _T_co]: ...
819
820 @classmethod
821 def _empty_constructor(cls, arg1: Any) -> Mapped[_T_co]: ...
822
823 def __set__(
824 self, instance: Any, value: Union[SQLCoreOperations[_T_co], _T_co]
825 ) -> None: ...
826
827 def __delete__(self, instance: Any) -> None: ...
828
829
830class _MappedAttribute(Generic[_T_co], TypingOnly):
831 """Mixin for attributes which should be replaced by mapper-assigned
832 attributes.
833
834 """
835
836 __slots__ = ()
837
838
839class _DeclarativeMapped(Mapped[_T_co], _MappedAttribute[_T_co]):
840 """Mixin for :class:`.MapperProperty` subclasses that allows them to
841 be compatible with ORM-annotated declarative mappings.
842
843 """
844
845 __slots__ = ()
846
847 # MappedSQLExpression, Relationship, Composite etc. dont actually do
848 # SQL expression behavior. yet there is code that compares them with
849 # __eq__(), __ne__(), etc. Since #8847 made Mapped even more full
850 # featured including ColumnOperators, we need to have those methods
851 # be no-ops for these objects, so return NotImplemented to fall back
852 # to normal comparison behavior.
853 def operate(self, op: OperatorType, *other: Any, **kwargs: Any) -> Any:
854 return NotImplemented
855
856 __sa_operate__ = operate
857
858 def reverse_operate(
859 self, op: OperatorType, other: Any, **kwargs: Any
860 ) -> Any:
861 return NotImplemented
862
863
864class DynamicMapped(_MappedAnnotationBase[_T_co]):
865 """Represent the ORM mapped attribute type for a "dynamic" relationship.
866
867 The :class:`_orm.DynamicMapped` type annotation may be used in an
868 :ref:`Annotated Declarative Table <orm_declarative_mapped_column>` mapping
869 to indicate that the ``lazy="dynamic"`` loader strategy should be used
870 for a particular :func:`_orm.relationship`.
871
872 .. legacy:: The "dynamic" lazy loader strategy is the legacy form of what
873 is now the "write_only" strategy described in the section
874 :ref:`write_only_relationship`.
875
876 E.g.::
877
878 class User(Base):
879 __tablename__ = "user"
880 id: Mapped[int] = mapped_column(primary_key=True)
881 addresses: DynamicMapped[Address] = relationship(
882 cascade="all,delete-orphan"
883 )
884
885 See the section :ref:`dynamic_relationship` for background.
886
887 .. versionadded:: 2.0
888
889 .. seealso::
890
891 :ref:`dynamic_relationship` - complete background
892
893 :class:`.WriteOnlyMapped` - fully 2.0 style version
894
895 """
896
897 __slots__ = ()
898
899 if TYPE_CHECKING:
900
901 @overload
902 def __get__(
903 self, instance: None, owner: Any
904 ) -> InstrumentedAttribute[_T_co]: ...
905
906 @overload
907 def __get__(
908 self, instance: object, owner: Any
909 ) -> AppenderQuery[_T_co]: ...
910
911 def __get__(
912 self, instance: Optional[object], owner: Any
913 ) -> Union[InstrumentedAttribute[_T_co], AppenderQuery[_T_co]]: ...
914
915 def __set__(
916 self, instance: Any, value: typing.Collection[_T_co]
917 ) -> None: ...
918
919
920class WriteOnlyMapped(_MappedAnnotationBase[_T_co]):
921 """Represent the ORM mapped attribute type for a "write only" relationship.
922
923 The :class:`_orm.WriteOnlyMapped` type annotation may be used in an
924 :ref:`Annotated Declarative Table <orm_declarative_mapped_column>` mapping
925 to indicate that the ``lazy="write_only"`` loader strategy should be used
926 for a particular :func:`_orm.relationship`.
927
928 E.g.::
929
930 class User(Base):
931 __tablename__ = "user"
932 id: Mapped[int] = mapped_column(primary_key=True)
933 addresses: WriteOnlyMapped[Address] = relationship(
934 cascade="all,delete-orphan"
935 )
936
937 See the section :ref:`write_only_relationship` for background.
938
939 .. versionadded:: 2.0
940
941 .. seealso::
942
943 :ref:`write_only_relationship` - complete background
944
945 :class:`.DynamicMapped` - includes legacy :class:`_orm.Query` support
946
947 """
948
949 __slots__ = ()
950
951 if TYPE_CHECKING:
952
953 @overload
954 def __get__(
955 self, instance: None, owner: Any
956 ) -> InstrumentedAttribute[_T_co]: ...
957
958 @overload
959 def __get__(
960 self, instance: object, owner: Any
961 ) -> WriteOnlyCollection[_T_co]: ...
962
963 def __get__(
964 self, instance: Optional[object], owner: Any
965 ) -> Union[
966 InstrumentedAttribute[_T_co], WriteOnlyCollection[_T_co]
967 ]: ...
968
969 def __set__(
970 self, instance: Any, value: typing.Collection[_T_co]
971 ) -> None: ...