1# orm/base.py
2# Copyright (C) 2005-2025 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_none_only_set = frozenset([None])
287
288_SET_DEFERRED_EXPIRED = util.symbol("SET_DEFERRED_EXPIRED")
289
290_DEFER_FOR_STATE = util.symbol("DEFER_FOR_STATE")
291
292_RAISE_FOR_STATE = util.symbol("RAISE_FOR_STATE")
293
294
295_F = TypeVar("_F", bound=Callable[..., Any])
296_Self = TypeVar("_Self")
297
298
299def _assertions(
300 *assertions: Any,
301) -> Callable[[_F], _F]:
302 @util.decorator
303 def generate(fn: _F, self: _Self, *args: Any, **kw: Any) -> _Self:
304 for assertion in assertions:
305 assertion(self, fn.__name__)
306 fn(self, *args, **kw)
307 return self
308
309 return generate
310
311
312if TYPE_CHECKING:
313
314 def manager_of_class(cls: Type[_O]) -> ClassManager[_O]: ...
315
316 @overload
317 def opt_manager_of_class(cls: AliasedClass[Any]) -> None: ...
318
319 @overload
320 def opt_manager_of_class(
321 cls: _ExternalEntityType[_O],
322 ) -> Optional[ClassManager[_O]]: ...
323
324 def opt_manager_of_class(
325 cls: _ExternalEntityType[_O],
326 ) -> Optional[ClassManager[_O]]: ...
327
328 def instance_state(instance: _O) -> InstanceState[_O]: ...
329
330 def instance_dict(instance: object) -> Dict[str, Any]: ...
331
332else:
333 # these can be replaced by sqlalchemy.ext.instrumentation
334 # if augmented class instrumentation is enabled.
335
336 def manager_of_class(cls):
337 try:
338 return cls.__dict__[DEFAULT_MANAGER_ATTR]
339 except KeyError as ke:
340 raise exc.UnmappedClassError(
341 cls, f"Can't locate an instrumentation manager for class {cls}"
342 ) from ke
343
344 def opt_manager_of_class(cls):
345 return cls.__dict__.get(DEFAULT_MANAGER_ATTR)
346
347 instance_state = operator.attrgetter(DEFAULT_STATE_ATTR)
348
349 instance_dict = operator.attrgetter("__dict__")
350
351
352def instance_str(instance: object) -> str:
353 """Return a string describing an instance."""
354
355 return state_str(instance_state(instance))
356
357
358def state_str(state: InstanceState[Any]) -> str:
359 """Return a string describing an instance via its InstanceState."""
360
361 if state is None:
362 return "None"
363 else:
364 return "<%s at 0x%x>" % (state.class_.__name__, id(state.obj()))
365
366
367def state_class_str(state: InstanceState[Any]) -> str:
368 """Return a string describing an instance's class via its
369 InstanceState.
370 """
371
372 if state is None:
373 return "None"
374 else:
375 return "<%s>" % (state.class_.__name__,)
376
377
378def attribute_str(instance: object, attribute: str) -> str:
379 return instance_str(instance) + "." + attribute
380
381
382def state_attribute_str(state: InstanceState[Any], attribute: str) -> str:
383 return state_str(state) + "." + attribute
384
385
386def object_mapper(instance: _T) -> Mapper[_T]:
387 """Given an object, return the primary Mapper associated with the object
388 instance.
389
390 Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError`
391 if no mapping is configured.
392
393 This function is available via the inspection system as::
394
395 inspect(instance).mapper
396
397 Using the inspection system will raise
398 :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is
399 not part of a mapping.
400
401 """
402 return object_state(instance).mapper
403
404
405def object_state(instance: _T) -> InstanceState[_T]:
406 """Given an object, return the :class:`.InstanceState`
407 associated with the object.
408
409 Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError`
410 if no mapping is configured.
411
412 Equivalent functionality is available via the :func:`_sa.inspect`
413 function as::
414
415 inspect(instance)
416
417 Using the inspection system will raise
418 :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is
419 not part of a mapping.
420
421 """
422 state = _inspect_mapped_object(instance)
423 if state is None:
424 raise exc.UnmappedInstanceError(instance)
425 else:
426 return state
427
428
429@inspection._inspects(object)
430def _inspect_mapped_object(instance: _T) -> Optional[InstanceState[_T]]:
431 try:
432 return instance_state(instance)
433 except (exc.UnmappedClassError,) + exc.NO_STATE:
434 return None
435
436
437def _class_to_mapper(
438 class_or_mapper: Union[Mapper[_T], Type[_T]]
439) -> Mapper[_T]:
440 # can't get mypy to see an overload for this
441 insp = inspection.inspect(class_or_mapper, False)
442 if insp is not None:
443 return insp.mapper # type: ignore
444 else:
445 assert isinstance(class_or_mapper, type)
446 raise exc.UnmappedClassError(class_or_mapper)
447
448
449def _mapper_or_none(
450 entity: Union[Type[_T], _InternalEntityType[_T]]
451) -> Optional[Mapper[_T]]:
452 """Return the :class:`_orm.Mapper` for the given class or None if the
453 class is not mapped.
454 """
455
456 # can't get mypy to see an overload for this
457 insp = inspection.inspect(entity, False)
458 if insp is not None:
459 return insp.mapper # type: ignore
460 else:
461 return None
462
463
464def _is_mapped_class(entity: Any) -> bool:
465 """Return True if the given object is a mapped class,
466 :class:`_orm.Mapper`, or :class:`.AliasedClass`.
467 """
468
469 insp = inspection.inspect(entity, False)
470 return (
471 insp is not None
472 and not insp.is_clause_element
473 and (insp.is_mapper or insp.is_aliased_class)
474 )
475
476
477def _is_aliased_class(entity: Any) -> bool:
478 insp = inspection.inspect(entity, False)
479 return insp is not None and getattr(insp, "is_aliased_class", False)
480
481
482@no_type_check
483def _entity_descriptor(entity: _EntityType[Any], key: str) -> Any:
484 """Return a class attribute given an entity and string name.
485
486 May return :class:`.InstrumentedAttribute` or user-defined
487 attribute.
488
489 """
490 insp = inspection.inspect(entity)
491 if insp.is_selectable:
492 description = entity
493 entity = insp.c
494 elif insp.is_aliased_class:
495 entity = insp.entity
496 description = entity
497 elif hasattr(insp, "mapper"):
498 description = entity = insp.mapper.class_
499 else:
500 description = entity
501
502 try:
503 return getattr(entity, key)
504 except AttributeError as err:
505 raise sa_exc.InvalidRequestError(
506 "Entity '%s' has no property '%s'" % (description, key)
507 ) from err
508
509
510if TYPE_CHECKING:
511
512 def _state_mapper(state: InstanceState[_O]) -> Mapper[_O]: ...
513
514else:
515 _state_mapper = util.dottedgetter("manager.mapper")
516
517
518def _inspect_mapped_class(
519 class_: Type[_O], configure: bool = False
520) -> Optional[Mapper[_O]]:
521 try:
522 class_manager = opt_manager_of_class(class_)
523 if class_manager is None or not class_manager.is_mapped:
524 return None
525 mapper = class_manager.mapper
526 except exc.NO_STATE:
527 return None
528 else:
529 if configure:
530 mapper._check_configure()
531 return mapper
532
533
534def _parse_mapper_argument(arg: Union[Mapper[_O], Type[_O]]) -> Mapper[_O]:
535 insp = inspection.inspect(arg, raiseerr=False)
536 if insp_is_mapper(insp):
537 return insp
538
539 raise sa_exc.ArgumentError(f"Mapper or mapped class expected, got {arg!r}")
540
541
542def class_mapper(class_: Type[_O], configure: bool = True) -> Mapper[_O]:
543 """Given a class, return the primary :class:`_orm.Mapper` associated
544 with the key.
545
546 Raises :exc:`.UnmappedClassError` if no mapping is configured
547 on the given class, or :exc:`.ArgumentError` if a non-class
548 object is passed.
549
550 Equivalent functionality is available via the :func:`_sa.inspect`
551 function as::
552
553 inspect(some_mapped_class)
554
555 Using the inspection system will raise
556 :class:`sqlalchemy.exc.NoInspectionAvailable` if the class is not mapped.
557
558 """
559 mapper = _inspect_mapped_class(class_, configure=configure)
560 if mapper is None:
561 if not isinstance(class_, type):
562 raise sa_exc.ArgumentError(
563 "Class object expected, got '%r'." % (class_,)
564 )
565 raise exc.UnmappedClassError(class_)
566 else:
567 return mapper
568
569
570class InspectionAttr:
571 """A base class applied to all ORM objects and attributes that are
572 related to things that can be returned by the :func:`_sa.inspect` function.
573
574 The attributes defined here allow the usage of simple boolean
575 checks to test basic facts about the object returned.
576
577 While the boolean checks here are basically the same as using
578 the Python isinstance() function, the flags here can be used without
579 the need to import all of these classes, and also such that
580 the SQLAlchemy class system can change while leaving the flags
581 here intact for forwards-compatibility.
582
583 """
584
585 __slots__: Tuple[str, ...] = ()
586
587 is_selectable = False
588 """Return True if this object is an instance of
589 :class:`_expression.Selectable`."""
590
591 is_aliased_class = False
592 """True if this object is an instance of :class:`.AliasedClass`."""
593
594 is_instance = False
595 """True if this object is an instance of :class:`.InstanceState`."""
596
597 is_mapper = False
598 """True if this object is an instance of :class:`_orm.Mapper`."""
599
600 is_bundle = False
601 """True if this object is an instance of :class:`.Bundle`."""
602
603 is_property = False
604 """True if this object is an instance of :class:`.MapperProperty`."""
605
606 is_attribute = False
607 """True if this object is a Python :term:`descriptor`.
608
609 This can refer to one of many types. Usually a
610 :class:`.QueryableAttribute` which handles attributes events on behalf
611 of a :class:`.MapperProperty`. But can also be an extension type
612 such as :class:`.AssociationProxy` or :class:`.hybrid_property`.
613 The :attr:`.InspectionAttr.extension_type` will refer to a constant
614 identifying the specific subtype.
615
616 .. seealso::
617
618 :attr:`_orm.Mapper.all_orm_descriptors`
619
620 """
621
622 _is_internal_proxy = False
623 """True if this object is an internal proxy object.
624
625 .. versionadded:: 1.2.12
626
627 """
628
629 is_clause_element = False
630 """True if this object is an instance of
631 :class:`_expression.ClauseElement`."""
632
633 extension_type: InspectionAttrExtensionType = NotExtension.NOT_EXTENSION
634 """The extension type, if any.
635 Defaults to :attr:`.interfaces.NotExtension.NOT_EXTENSION`
636
637 .. seealso::
638
639 :class:`.HybridExtensionType`
640
641 :class:`.AssociationProxyExtensionType`
642
643 """
644
645
646class InspectionAttrInfo(InspectionAttr):
647 """Adds the ``.info`` attribute to :class:`.InspectionAttr`.
648
649 The rationale for :class:`.InspectionAttr` vs. :class:`.InspectionAttrInfo`
650 is that the former is compatible as a mixin for classes that specify
651 ``__slots__``; this is essentially an implementation artifact.
652
653 """
654
655 __slots__ = ()
656
657 @util.ro_memoized_property
658 def info(self) -> _InfoType:
659 """Info dictionary associated with the object, allowing user-defined
660 data to be associated with this :class:`.InspectionAttr`.
661
662 The dictionary is generated when first accessed. Alternatively,
663 it can be specified as a constructor argument to the
664 :func:`.column_property`, :func:`_orm.relationship`, or
665 :func:`.composite`
666 functions.
667
668 .. seealso::
669
670 :attr:`.QueryableAttribute.info`
671
672 :attr:`.SchemaItem.info`
673
674 """
675 return {}
676
677
678class SQLORMOperations(SQLCoreOperations[_T_co], TypingOnly):
679 __slots__ = ()
680
681 if typing.TYPE_CHECKING:
682
683 def of_type(
684 self, class_: _EntityType[Any]
685 ) -> PropComparator[_T_co]: ...
686
687 def and_(
688 self, *criteria: _ColumnExpressionArgument[bool]
689 ) -> PropComparator[bool]: ...
690
691 def any( # noqa: A001
692 self,
693 criterion: Optional[_ColumnExpressionArgument[bool]] = None,
694 **kwargs: Any,
695 ) -> ColumnElement[bool]: ...
696
697 def has(
698 self,
699 criterion: Optional[_ColumnExpressionArgument[bool]] = None,
700 **kwargs: Any,
701 ) -> ColumnElement[bool]: ...
702
703
704class ORMDescriptor(Generic[_T_co], TypingOnly):
705 """Represent any Python descriptor that provides a SQL expression
706 construct at the class level."""
707
708 __slots__ = ()
709
710 if typing.TYPE_CHECKING:
711
712 @overload
713 def __get__(
714 self, instance: Any, owner: Literal[None]
715 ) -> ORMDescriptor[_T_co]: ...
716
717 @overload
718 def __get__(
719 self, instance: Literal[None], owner: Any
720 ) -> SQLCoreOperations[_T_co]: ...
721
722 @overload
723 def __get__(self, instance: object, owner: Any) -> _T_co: ...
724
725 def __get__(
726 self, instance: object, owner: Any
727 ) -> Union[ORMDescriptor[_T_co], SQLCoreOperations[_T_co], _T_co]: ...
728
729
730class _MappedAnnotationBase(Generic[_T_co], TypingOnly):
731 """common class for Mapped and similar ORM container classes.
732
733 these are classes that can appear on the left side of an ORM declarative
734 mapping, containing a mapped class or in some cases a collection
735 surrounding a mapped class.
736
737 """
738
739 __slots__ = ()
740
741
742class SQLORMExpression(
743 SQLORMOperations[_T_co], SQLColumnExpression[_T_co], TypingOnly
744):
745 """A type that may be used to indicate any ORM-level attribute or
746 object that acts in place of one, in the context of SQL expression
747 construction.
748
749 :class:`.SQLORMExpression` extends from the Core
750 :class:`.SQLColumnExpression` to add additional SQL methods that are ORM
751 specific, such as :meth:`.PropComparator.of_type`, and is part of the bases
752 for :class:`.InstrumentedAttribute`. It may be used in :pep:`484` typing to
753 indicate arguments or return values that should behave as ORM-level
754 attribute expressions.
755
756 .. versionadded:: 2.0.0b4
757
758
759 """
760
761 __slots__ = ()
762
763
764class Mapped(
765 SQLORMExpression[_T_co],
766 ORMDescriptor[_T_co],
767 _MappedAnnotationBase[_T_co],
768 roles.DDLConstraintColumnRole,
769):
770 """Represent an ORM mapped attribute on a mapped class.
771
772 This class represents the complete descriptor interface for any class
773 attribute that will have been :term:`instrumented` by the ORM
774 :class:`_orm.Mapper` class. Provides appropriate information to type
775 checkers such as pylance and mypy so that ORM-mapped attributes
776 are correctly typed.
777
778 The most prominent use of :class:`_orm.Mapped` is in
779 the :ref:`Declarative Mapping <orm_explicit_declarative_base>` form
780 of :class:`_orm.Mapper` configuration, where used explicitly it drives
781 the configuration of ORM attributes such as :func:`_orm.mapped_class`
782 and :func:`_orm.relationship`.
783
784 .. seealso::
785
786 :ref:`orm_explicit_declarative_base`
787
788 :ref:`orm_declarative_table`
789
790 .. tip::
791
792 The :class:`_orm.Mapped` class represents attributes that are handled
793 directly by the :class:`_orm.Mapper` class. It does not include other
794 Python descriptor classes that are provided as extensions, including
795 :ref:`hybrids_toplevel` and the :ref:`associationproxy_toplevel`.
796 While these systems still make use of ORM-specific superclasses
797 and structures, they are not :term:`instrumented` by the
798 :class:`_orm.Mapper` and instead provide their own functionality
799 when they are accessed on a class.
800
801 .. versionadded:: 1.4
802
803
804 """
805
806 __slots__ = ()
807
808 if typing.TYPE_CHECKING:
809
810 @overload
811 def __get__(
812 self, instance: None, owner: Any
813 ) -> InstrumentedAttribute[_T_co]: ...
814
815 @overload
816 def __get__(self, instance: object, owner: Any) -> _T_co: ...
817
818 def __get__(
819 self, instance: Optional[object], owner: Any
820 ) -> Union[InstrumentedAttribute[_T_co], _T_co]: ...
821
822 @classmethod
823 def _empty_constructor(cls, arg1: Any) -> Mapped[_T_co]: ...
824
825 def __set__(
826 self, instance: Any, value: Union[SQLCoreOperations[_T_co], _T_co]
827 ) -> None: ...
828
829 def __delete__(self, instance: Any) -> None: ...
830
831
832class _MappedAttribute(Generic[_T_co], TypingOnly):
833 """Mixin for attributes which should be replaced by mapper-assigned
834 attributes.
835
836 """
837
838 __slots__ = ()
839
840
841class _DeclarativeMapped(Mapped[_T_co], _MappedAttribute[_T_co]):
842 """Mixin for :class:`.MapperProperty` subclasses that allows them to
843 be compatible with ORM-annotated declarative mappings.
844
845 """
846
847 __slots__ = ()
848
849 # MappedSQLExpression, Relationship, Composite etc. dont actually do
850 # SQL expression behavior. yet there is code that compares them with
851 # __eq__(), __ne__(), etc. Since #8847 made Mapped even more full
852 # featured including ColumnOperators, we need to have those methods
853 # be no-ops for these objects, so return NotImplemented to fall back
854 # to normal comparison behavior.
855 def operate(self, op: OperatorType, *other: Any, **kwargs: Any) -> Any:
856 return NotImplemented
857
858 __sa_operate__ = operate
859
860 def reverse_operate(
861 self, op: OperatorType, other: Any, **kwargs: Any
862 ) -> Any:
863 return NotImplemented
864
865
866class DynamicMapped(_MappedAnnotationBase[_T_co]):
867 """Represent the ORM mapped attribute type for a "dynamic" relationship.
868
869 The :class:`_orm.DynamicMapped` type annotation may be used in an
870 :ref:`Annotated Declarative Table <orm_declarative_mapped_column>` mapping
871 to indicate that the ``lazy="dynamic"`` loader strategy should be used
872 for a particular :func:`_orm.relationship`.
873
874 .. legacy:: The "dynamic" lazy loader strategy is the legacy form of what
875 is now the "write_only" strategy described in the section
876 :ref:`write_only_relationship`.
877
878 E.g.::
879
880 class User(Base):
881 __tablename__ = "user"
882 id: Mapped[int] = mapped_column(primary_key=True)
883 addresses: DynamicMapped[Address] = relationship(
884 cascade="all,delete-orphan"
885 )
886
887 See the section :ref:`dynamic_relationship` for background.
888
889 .. versionadded:: 2.0
890
891 .. seealso::
892
893 :ref:`dynamic_relationship` - complete background
894
895 :class:`.WriteOnlyMapped` - fully 2.0 style version
896
897 """
898
899 __slots__ = ()
900
901 if TYPE_CHECKING:
902
903 @overload
904 def __get__(
905 self, instance: None, owner: Any
906 ) -> InstrumentedAttribute[_T_co]: ...
907
908 @overload
909 def __get__(
910 self, instance: object, owner: Any
911 ) -> AppenderQuery[_T_co]: ...
912
913 def __get__(
914 self, instance: Optional[object], owner: Any
915 ) -> Union[InstrumentedAttribute[_T_co], AppenderQuery[_T_co]]: ...
916
917 def __set__(
918 self, instance: Any, value: typing.Collection[_T_co]
919 ) -> None: ...
920
921
922class WriteOnlyMapped(_MappedAnnotationBase[_T_co]):
923 """Represent the ORM mapped attribute type for a "write only" relationship.
924
925 The :class:`_orm.WriteOnlyMapped` type annotation may be used in an
926 :ref:`Annotated Declarative Table <orm_declarative_mapped_column>` mapping
927 to indicate that the ``lazy="write_only"`` loader strategy should be used
928 for a particular :func:`_orm.relationship`.
929
930 E.g.::
931
932 class User(Base):
933 __tablename__ = "user"
934 id: Mapped[int] = mapped_column(primary_key=True)
935 addresses: WriteOnlyMapped[Address] = relationship(
936 cascade="all,delete-orphan"
937 )
938
939 See the section :ref:`write_only_relationship` for background.
940
941 .. versionadded:: 2.0
942
943 .. seealso::
944
945 :ref:`write_only_relationship` - complete background
946
947 :class:`.DynamicMapped` - includes legacy :class:`_orm.Query` support
948
949 """
950
951 __slots__ = ()
952
953 if TYPE_CHECKING:
954
955 @overload
956 def __get__(
957 self, instance: None, owner: Any
958 ) -> InstrumentedAttribute[_T_co]: ...
959
960 @overload
961 def __get__(
962 self, instance: object, owner: Any
963 ) -> WriteOnlyCollection[_T_co]: ...
964
965 def __get__(
966 self, instance: Optional[object], owner: Any
967 ) -> Union[
968 InstrumentedAttribute[_T_co], WriteOnlyCollection[_T_co]
969 ]: ...
970
971 def __set__(
972 self, instance: Any, value: typing.Collection[_T_co]
973 ) -> None: ...