1# orm/decl_api.py
2# Copyright (C) 2005-2026 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"""Public API functions and helpers for declarative."""
9
10from __future__ import annotations
11
12import re
13import typing
14from typing import Any
15from typing import Callable
16from typing import ClassVar
17from typing import Dict
18from typing import FrozenSet
19from typing import Generic
20from typing import Iterable
21from typing import Iterator
22from typing import Literal
23from typing import Mapping
24from typing import Optional
25from typing import overload
26from typing import Protocol
27from typing import Set
28from typing import Tuple
29from typing import Type
30from typing import TYPE_CHECKING
31from typing import TypeVar
32from typing import Union
33import weakref
34
35from . import attributes
36from . import clsregistry
37from . import instrumentation
38from . import interfaces
39from . import mapperlib
40from ._orm_constructors import composite
41from ._orm_constructors import deferred
42from ._orm_constructors import mapped_column
43from ._orm_constructors import relationship
44from ._orm_constructors import synonym
45from .attributes import InstrumentedAttribute
46from .base import _inspect_mapped_class
47from .base import _is_mapped_class
48from .base import Mapped
49from .base import ORMDescriptor
50from .decl_base import _add_attribute
51from .decl_base import _declarative_constructor
52from .decl_base import _DeclarativeMapperConfig
53from .decl_base import _DeferredDeclarativeConfig
54from .decl_base import _del_attribute
55from .decl_base import _ORMClassConfigurator
56from .decl_base import MappedClassProtocol
57from .descriptor_props import Composite
58from .descriptor_props import Synonym
59from .descriptor_props import Synonym as _orm_synonym
60from .mapper import Mapper
61from .properties import MappedColumn
62from .relationships import RelationshipProperty
63from .state import InstanceState
64from .. import exc
65from .. import inspection
66from .. import util
67from ..event import dispatcher
68from ..event import EventTarget
69from ..sql import sqltypes
70from ..sql._annotated_cols import _TC
71from ..sql.base import _NoArg
72from ..sql.elements import SQLCoreOperations
73from ..sql.schema import MetaData
74from ..sql.selectable import FromClause
75from ..util import hybridmethod
76from ..util import hybridproperty
77from ..util import typing as compat_typing
78from ..util import TypingOnly
79from ..util.typing import CallableReference
80from ..util.typing import de_optionalize_union_types
81from ..util.typing import GenericProtocol
82from ..util.typing import is_generic
83from ..util.typing import is_literal
84from ..util.typing import LITERAL_TYPES
85from ..util.typing import Self
86from ..util.typing import TypeAliasType
87
88if TYPE_CHECKING:
89 from ._typing import _O
90 from ._typing import _RegistryType
91 from .instrumentation import ClassManager
92 from .interfaces import _DataclassArguments
93 from .interfaces import MapperProperty
94 from .state import InstanceState # noqa
95 from ..sql._typing import _TypeEngineArgument
96 from ..util.typing import _MatchedOnType
97
98_T = TypeVar("_T", bound=Any)
99_T_co = TypeVar("_T_co", bound=Any, covariant=True)
100
101_TT = TypeVar("_TT", bound=Any)
102
103# it's not clear how to have Annotated, Union objects etc. as keys here
104# from a typing perspective so just leave it open ended for now
105_TypeAnnotationMapType = Mapping[Any, "_TypeEngineArgument[Any]"]
106_MutableTypeAnnotationMapType = Dict[Any, "_TypeEngineArgument[Any]"]
107
108_DeclaredAttrDecorated = Callable[
109 ..., Union[Mapped[_T_co], ORMDescriptor[_T_co], SQLCoreOperations[_T_co]]
110]
111
112
113def has_inherited_table(cls: Type[_O]) -> bool:
114 """Given a class, return True if any of the classes it inherits from has a
115 mapped table, otherwise return False.
116
117 This is used in declarative mixins to build attributes that behave
118 differently for the base class vs. a subclass in an inheritance
119 hierarchy.
120
121 .. seealso::
122
123 :ref:`decl_mixin_inheritance`
124
125 """
126 for class_ in cls.__mro__[1:]:
127 if getattr(class_, "__table__", None) is not None:
128 return True
129 return False
130
131
132class _DynamicAttributesType(type):
133 def __setattr__(cls, key: str, value: Any) -> None:
134 if "__mapper__" in cls.__dict__:
135 _add_attribute(cls, key, value)
136 else:
137 type.__setattr__(cls, key, value)
138
139 def __delattr__(cls, key: str) -> None:
140 if "__mapper__" in cls.__dict__:
141 _del_attribute(cls, key)
142 else:
143 type.__delattr__(cls, key)
144
145
146class DeclarativeAttributeIntercept(
147 _DynamicAttributesType,
148 # Inspectable is used only by the mypy plugin
149 inspection.Inspectable[Mapper[Any]],
150):
151 """Metaclass that may be used in conjunction with the
152 :class:`_orm.DeclarativeBase` class to support addition of class
153 attributes dynamically.
154
155 """
156
157
158@compat_typing.dataclass_transform(
159 field_specifiers=(
160 MappedColumn,
161 RelationshipProperty,
162 Composite,
163 Synonym,
164 mapped_column,
165 relationship,
166 composite,
167 synonym,
168 deferred,
169 ),
170)
171class DCTransformDeclarative(DeclarativeAttributeIntercept):
172 """metaclass that includes @dataclass_transforms"""
173
174
175class DeclarativeMeta(DeclarativeAttributeIntercept):
176 metadata: MetaData
177 registry: RegistryType
178
179 def __init__(
180 cls, classname: Any, bases: Any, dict_: Any, **kw: Any
181 ) -> None:
182 # use cls.__dict__, which can be modified by an
183 # __init_subclass__() method (#7900)
184 dict_ = cls.__dict__
185
186 # early-consume registry from the initial declarative base,
187 # assign privately to not conflict with subclass attributes named
188 # "registry"
189 reg = getattr(cls, "_sa_registry", None)
190 if reg is None:
191 reg = dict_.get("registry", None)
192 if not isinstance(reg, registry):
193 raise exc.InvalidRequestError(
194 "Declarative base class has no 'registry' attribute, "
195 "or registry is not a sqlalchemy.orm.registry() object"
196 )
197 else:
198 cls._sa_registry = reg
199
200 if not cls.__dict__.get("__abstract__", False):
201 _ORMClassConfigurator._as_declarative(reg, cls, dict_)
202 type.__init__(cls, classname, bases, dict_)
203
204
205def synonym_for(
206 name: str, map_column: bool = False
207) -> Callable[[Callable[..., Any]], Synonym[Any]]:
208 """Decorator that produces an :func:`_orm.synonym`
209 attribute in conjunction with a Python descriptor.
210
211 The function being decorated is passed to :func:`_orm.synonym` as the
212 :paramref:`.orm.synonym.descriptor` parameter::
213
214 class MyClass(Base):
215 __tablename__ = "my_table"
216
217 id = Column(Integer, primary_key=True)
218 _job_status = Column("job_status", String(50))
219
220 @synonym_for("job_status")
221 @property
222 def job_status(self):
223 return "Status: %s" % self._job_status
224
225 The :ref:`hybrid properties <mapper_hybrids>` feature of SQLAlchemy
226 is typically preferred instead of synonyms, which is a more legacy
227 feature.
228
229 .. seealso::
230
231 :ref:`synonyms` - Overview of synonyms
232
233 :func:`_orm.synonym` - the mapper-level function
234
235 :ref:`mapper_hybrids` - The Hybrid Attribute extension provides an
236 updated approach to augmenting attribute behavior more flexibly than
237 can be achieved with synonyms.
238
239 """
240
241 def decorate(fn: Callable[..., Any]) -> Synonym[Any]:
242 return _orm_synonym(name, map_column=map_column, descriptor=fn)
243
244 return decorate
245
246
247class _declared_attr_common:
248 def __init__(
249 self,
250 fn: Callable[..., Any],
251 cascading: bool = False,
252 quiet: bool = False,
253 ):
254 # support
255 # @declared_attr
256 # @classmethod
257 # def foo(cls) -> Mapped[thing]:
258 # ...
259 # which seems to help typing tools interpret the fn as a classmethod
260 # for situations where needed
261 if isinstance(fn, classmethod):
262 fn = fn.__func__
263
264 self.fget = fn
265 self._cascading = cascading
266 self._quiet = quiet
267 self.__doc__ = fn.__doc__
268
269 def _collect_return_annotation(self) -> Optional[Type[Any]]:
270 return util.get_annotations(self.fget).get("return")
271
272 def __get__(self, instance: Optional[object], owner: Any) -> Any:
273 # the declared_attr needs to make use of a cache that exists
274 # for the span of the declarative scan_attributes() phase.
275 # to achieve this we look at the class manager that's configured.
276
277 # note this method should not be called outside of the declarative
278 # setup phase
279
280 cls = owner
281 manager = attributes.opt_manager_of_class(cls)
282 if manager is None:
283 if not re.match(r"^__.+__$", self.fget.__name__):
284 # if there is no manager at all, then this class hasn't been
285 # run through declarative or mapper() at all, emit a warning.
286 util.warn(
287 "Unmanaged access of declarative attribute %s from "
288 "non-mapped class %s" % (self.fget.__name__, cls.__name__)
289 )
290 return self.fget(cls)
291 elif manager.is_mapped:
292 # the class is mapped, which means we're outside of the declarative
293 # scan setup, just run the function.
294 return self.fget(cls)
295
296 # here, we are inside of the declarative scan. use the registry
297 # that is tracking the values of these attributes.
298 declarative_scan = manager.declarative_scan()
299
300 # assert that we are in fact in the declarative scan
301 assert declarative_scan is not None
302
303 reg = declarative_scan.declared_attr_reg
304
305 if self in reg:
306 return reg[self]
307 else:
308 reg[self] = obj = self.fget(cls)
309 return obj
310
311
312class _declared_directive(_declared_attr_common, Generic[_T]):
313 # see mapping_api.rst for docstring
314
315 if typing.TYPE_CHECKING:
316
317 def __init__(
318 self,
319 fn: Callable[..., _T],
320 cascading: bool = False,
321 ): ...
322
323 def __get__(self, instance: Optional[object], owner: Any) -> _T: ...
324
325 def __set__(self, instance: Any, value: Any) -> None: ...
326
327 def __delete__(self, instance: Any) -> None: ...
328
329 def __call__(self, fn: Callable[..., _TT]) -> _declared_directive[_TT]:
330 # extensive fooling of mypy underway...
331 ...
332
333
334class declared_attr(interfaces._MappedAttribute[_T_co], _declared_attr_common):
335 """Mark a class-level method as representing the definition of
336 a mapped property or Declarative directive.
337
338 :class:`_orm.declared_attr` is typically applied as a decorator to a class
339 level method, turning the attribute into a scalar-like property that can be
340 invoked from the uninstantiated class. The Declarative mapping process
341 looks for these :class:`_orm.declared_attr` callables as it scans classes,
342 and assumes any attribute marked with :class:`_orm.declared_attr` will be a
343 callable that will produce an object specific to the Declarative mapping or
344 table configuration.
345
346 :class:`_orm.declared_attr` is usually applicable to
347 :ref:`mixins <orm_mixins_toplevel>`, to define relationships that are to be
348 applied to different implementors of the class. It may also be used to
349 define dynamically generated column expressions and other Declarative
350 attributes.
351
352 Example::
353
354 class ProvidesUserMixin:
355 "A mixin that adds a 'user' relationship to classes."
356
357 user_id: Mapped[int] = mapped_column(ForeignKey("user_table.id"))
358
359 @declared_attr
360 def user(cls) -> Mapped["User"]:
361 return relationship("User")
362
363 When used with Declarative directives such as ``__tablename__``, the
364 :meth:`_orm.declared_attr.directive` modifier may be used which indicates
365 to :pep:`484` typing tools that the given method is not dealing with
366 :class:`_orm.Mapped` attributes::
367
368 class CreateTableName:
369 @declared_attr.directive
370 def __tablename__(cls) -> str:
371 return cls.__name__.lower()
372
373 :class:`_orm.declared_attr` can also be applied directly to mapped
374 classes, to allow for attributes that dynamically configure themselves
375 on subclasses when using mapped inheritance schemes. Below
376 illustrates :class:`_orm.declared_attr` to create a dynamic scheme
377 for generating the :paramref:`_orm.Mapper.polymorphic_identity` parameter
378 for subclasses::
379
380 class Employee(Base):
381 __tablename__ = "employee"
382
383 id: Mapped[int] = mapped_column(primary_key=True)
384 type: Mapped[str] = mapped_column(String(50))
385
386 @declared_attr.directive
387 def __mapper_args__(cls) -> Dict[str, Any]:
388 if cls.__name__ == "Employee":
389 return {
390 "polymorphic_on": cls.type,
391 "polymorphic_identity": "Employee",
392 }
393 else:
394 return {"polymorphic_identity": cls.__name__}
395
396
397 class Engineer(Employee):
398 pass
399
400 :class:`_orm.declared_attr` supports decorating functions that are
401 explicitly decorated with ``@classmethod``. This is never necessary from a
402 runtime perspective, however may be needed in order to support :pep:`484`
403 typing tools that don't otherwise recognize the decorated function as
404 having class-level behaviors for the ``cls`` parameter::
405
406 class SomethingMixin:
407 x: Mapped[int]
408 y: Mapped[int]
409
410 @declared_attr
411 @classmethod
412 def x_plus_y(cls) -> Mapped[int]:
413 return column_property(cls.x + cls.y)
414
415 .. versionadded:: 2.0 - :class:`_orm.declared_attr` can accommodate a
416 function decorated with ``@classmethod`` to help with :pep:`484`
417 integration where needed.
418
419
420 .. seealso::
421
422 :ref:`orm_mixins_toplevel` - Declarative Mixin documentation with
423 background on use patterns for :class:`_orm.declared_attr`.
424
425 """ # noqa: E501
426
427 if typing.TYPE_CHECKING:
428
429 def __init__(
430 self,
431 fn: _DeclaredAttrDecorated[_T_co],
432 cascading: bool = False,
433 ): ...
434
435 def __set__(self, instance: Any, value: Any) -> None: ...
436
437 def __delete__(self, instance: Any) -> None: ...
438
439 # this is the Mapped[] API where at class descriptor get time we want
440 # the type checker to see InstrumentedAttribute[_T]. However the
441 # callable function prior to mapping in fact calls the given
442 # declarative function that does not return InstrumentedAttribute
443 @overload
444 def __get__(
445 self, instance: None, owner: Any
446 ) -> InstrumentedAttribute[_T_co]: ...
447
448 @overload
449 def __get__(self, instance: object, owner: Any) -> _T_co: ...
450
451 def __get__(
452 self, instance: Optional[object], owner: Any
453 ) -> Union[InstrumentedAttribute[_T_co], _T_co]: ...
454
455 @hybridmethod
456 def _stateful(cls, **kw: Any) -> _stateful_declared_attr[_T_co]:
457 return _stateful_declared_attr(**kw)
458
459 @hybridproperty
460 def directive(cls) -> _declared_directive[Any]:
461 # see mapping_api.rst for docstring
462 return _declared_directive # type: ignore
463
464 @hybridproperty
465 def cascading(cls) -> _stateful_declared_attr[_T_co]:
466 # see mapping_api.rst for docstring
467 return cls._stateful(cascading=True)
468
469
470class _stateful_declared_attr(declared_attr[_T_co]):
471 kw: Dict[str, Any]
472
473 def __init__(self, **kw: Any):
474 self.kw = kw
475
476 @hybridmethod
477 def _stateful(self, **kw: Any) -> _stateful_declared_attr[_T_co]:
478 new_kw = self.kw.copy()
479 new_kw.update(kw)
480 return _stateful_declared_attr(**new_kw)
481
482 def __call__(
483 self, fn: _DeclaredAttrDecorated[_T_co]
484 ) -> declared_attr[_T_co]:
485 return declared_attr(fn, **self.kw)
486
487
488@util.deprecated(
489 "2.1",
490 "The declarative_mixin decorator was used only by the now removed "
491 "mypy plugin so it has no longer any use and can be safely removed.",
492)
493def declarative_mixin(cls: Type[_T]) -> Type[_T]:
494 """Mark a class as providing the feature of "declarative mixin".
495
496 E.g.::
497
498 from sqlalchemy.orm import declared_attr
499 from sqlalchemy.orm import declarative_mixin
500
501
502 @declarative_mixin
503 class MyMixin:
504
505 @declared_attr
506 def __tablename__(cls):
507 return cls.__name__.lower()
508
509 __table_args__ = {"mysql_engine": "InnoDB"}
510 __mapper_args__ = {"always_refresh": True}
511
512 id = Column(Integer, primary_key=True)
513
514
515 class MyModel(MyMixin, Base):
516 name = Column(String(1000))
517
518 The :func:`_orm.declarative_mixin` decorator currently does not modify
519 the given class in any way; it's current purpose is strictly to assist
520 the Mypy plugin in being able to identify
521 SQLAlchemy declarative mixin classes when no other context is present.
522
523 .. versionadded:: 1.4.6
524
525 .. seealso::
526
527 :ref:`orm_mixins_toplevel`
528
529 """ # noqa: E501
530
531 return cls
532
533
534def _setup_declarative_base(cls: Type[Any]) -> None:
535 metadata = getattr(cls, "metadata", None)
536 type_annotation_map = getattr(cls, "type_annotation_map", None)
537 reg = getattr(cls, "registry", None)
538
539 if reg is not None:
540 if not isinstance(reg, registry):
541 raise exc.InvalidRequestError(
542 "Declarative base class has a 'registry' attribute that is "
543 "not an instance of sqlalchemy.orm.registry()"
544 )
545 elif type_annotation_map is not None:
546 raise exc.InvalidRequestError(
547 "Declarative base class has both a 'registry' attribute and a "
548 "type_annotation_map entry. Per-base type_annotation_maps "
549 "are not supported. Please apply the type_annotation_map "
550 "to this registry directly."
551 )
552
553 else:
554 reg = registry(
555 metadata=metadata, type_annotation_map=type_annotation_map
556 )
557 cls.registry = reg
558
559 cls._sa_registry = reg
560
561 if "metadata" not in cls.__dict__:
562 cls.metadata = cls.registry.metadata
563
564 if getattr(cls, "__init__", object.__init__) is object.__init__:
565 cls.__init__ = cls.registry.constructor
566
567
568def _generate_dc_transforms(
569 cls_: Type[_O],
570 init: Union[_NoArg, bool] = _NoArg.NO_ARG,
571 repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
572 eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
573 order: Union[_NoArg, bool] = _NoArg.NO_ARG,
574 unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
575 match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
576 kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
577 dataclass_callable: Union[
578 _NoArg, Callable[..., Type[Any]]
579 ] = _NoArg.NO_ARG,
580) -> None:
581 apply_dc_transforms: _DataclassArguments = {
582 "init": init,
583 "repr": repr,
584 "eq": eq,
585 "order": order,
586 "unsafe_hash": unsafe_hash,
587 "match_args": match_args,
588 "kw_only": kw_only,
589 "dataclass_callable": dataclass_callable,
590 }
591
592 if hasattr(cls_, "_sa_apply_dc_transforms"):
593 current = cls_._sa_apply_dc_transforms # type: ignore[attr-defined]
594
595 _DeclarativeMapperConfig._assert_dc_arguments(current)
596
597 cls_._sa_apply_dc_transforms = { # type: ignore # noqa: E501
598 k: current.get(k, _NoArg.NO_ARG) if v is _NoArg.NO_ARG else v
599 for k, v in apply_dc_transforms.items()
600 }
601 else:
602 setattr(cls_, "_sa_apply_dc_transforms", apply_dc_transforms)
603
604
605class MappedAsDataclass(metaclass=DCTransformDeclarative):
606 """Mixin class to indicate when mapping this class, also convert it to be
607 a dataclass.
608
609 .. seealso::
610
611 :ref:`orm_declarative_native_dataclasses` - complete background
612 on SQLAlchemy native dataclass mapping with
613 :class:`_orm.MappedAsDataclass`.
614
615 :ref:`orm_declarative_dc_mixins` - examples specific to using
616 :class:`_orm.MappedAsDataclass` to create mixins
617
618 :func:`_orm.mapped_as_dataclass` / :func:`_orm.unmapped_dataclass` -
619 decorator versions with equivalent functionality
620
621 .. versionadded:: 2.0
622
623 """
624
625 def __init_subclass__(
626 cls,
627 init: Union[_NoArg, bool] = _NoArg.NO_ARG,
628 repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
629 eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
630 order: Union[_NoArg, bool] = _NoArg.NO_ARG,
631 unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
632 match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
633 kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
634 dataclass_callable: Union[
635 _NoArg, Callable[..., Type[Any]]
636 ] = _NoArg.NO_ARG,
637 **kw: Any,
638 ) -> None:
639 _generate_dc_transforms(
640 init=init,
641 repr=repr,
642 eq=eq,
643 order=order,
644 unsafe_hash=unsafe_hash,
645 match_args=match_args,
646 kw_only=kw_only,
647 dataclass_callable=dataclass_callable,
648 cls_=cls,
649 )
650 super().__init_subclass__(**kw)
651
652 if not _is_mapped_class(cls):
653 # turn unmapped classes into "good enough" dataclasses to serve
654 # as a base or a mixin
655 _ORMClassConfigurator._as_unmapped_dataclass(cls, cls.__dict__)
656
657
658class _DeclarativeTyping(TypingOnly):
659 """Common typing annotations shared by the DeclarativeBase and
660 DeclarativeBaseNoMeta classes.
661 """
662
663 __slots__ = ()
664
665 if typing.TYPE_CHECKING:
666 # protocols for inspection
667 def _sa_inspect_type(self) -> Mapper[Self]: ...
668
669 def _sa_inspect_instance(self) -> InstanceState[Self]: ...
670
671 # internal stuff
672 _sa_registry: ClassVar[_RegistryType]
673
674 # public interface
675 registry: ClassVar[_RegistryType]
676 """Refers to the :class:`_orm.registry` in use where new
677 :class:`_orm.Mapper` objects will be associated."""
678
679 metadata: ClassVar[MetaData]
680 """Refers to the :class:`_schema.MetaData` collection that will be used
681 for new :class:`_schema.Table` objects.
682
683 .. seealso::
684
685 :ref:`orm_declarative_metadata`
686
687 """
688
689 __name__: ClassVar[str]
690
691 # this ideally should be Mapper[Self], but mypy as of 1.4.1 does not
692 # like it, and breaks the declared_attr_one test. Pyright/pylance is
693 # ok with it.
694 __mapper__: ClassVar[Mapper[Any]]
695 """The :class:`_orm.Mapper` object to which a particular class is
696 mapped.
697
698 May also be acquired using :func:`_sa.inspect`, e.g.
699 ``inspect(klass)``.
700
701 """
702
703 __table__: ClassVar[FromClause]
704 """The :class:`_sql.FromClause` to which a particular subclass is
705 mapped.
706
707 This is usually an instance of :class:`_schema.Table` but may also
708 refer to other kinds of :class:`_sql.FromClause` such as
709 :class:`_sql.Subquery`, depending on how the class is mapped.
710
711 .. seealso::
712
713 :ref:`orm_declarative_metadata`
714
715 """
716
717 # pyright/pylance do not consider a classmethod a ClassVar so use Any
718 # https://github.com/microsoft/pylance-release/issues/3484
719 __tablename__: Any
720 """String name to assign to the generated
721 :class:`_schema.Table` object, if not specified directly via
722 :attr:`_orm.DeclarativeBase.__table__`.
723
724 .. seealso::
725
726 :ref:`orm_declarative_table`
727
728 """
729
730 __mapper_args__: Any
731 """Dictionary of arguments which will be passed to the
732 :class:`_orm.Mapper` constructor.
733
734 .. seealso::
735
736 :ref:`orm_declarative_mapper_options`
737
738 """
739
740 __table_args__: Any
741 """A dictionary or tuple of arguments that will be passed to the
742 :class:`_schema.Table` constructor. See
743 :ref:`orm_declarative_table_configuration`
744 for background on the specific structure of this collection.
745
746 .. seealso::
747
748 :ref:`orm_declarative_table_configuration`
749
750 """
751
752 def __init__(self, **kw: Any): ...
753
754
755class MappedClassWithTypedColumnsProtocol(Protocol[_TC]):
756 """An ORM mapped class that also defines in the ``__typed_cols__``
757 attribute its typed columns.
758
759 .. versionadded:: 2.1.0b2
760 """
761
762 __typed_cols__: _TC
763 """The :class:`_schema.TypedColumns` of this ORM mapped class."""
764
765 __name__: ClassVar[str]
766 __mapper__: ClassVar[Mapper[Any]]
767 __table__: ClassVar[FromClause]
768
769
770@overload
771def as_typed_table(
772 cls: type[MappedClassWithTypedColumnsProtocol[_TC]], /
773) -> FromClause[_TC]: ...
774
775
776@overload
777def as_typed_table(
778 cls: MappedClassProtocol[Any], typed_columns_cls: type[_TC], /
779) -> FromClause[_TC]: ...
780
781
782def as_typed_table(
783 cls: (
784 MappedClassProtocol[Any]
785 | type[MappedClassWithTypedColumnsProtocol[Any]]
786 ),
787 typed_columns_cls: Any = None,
788 /,
789) -> FromClause[Any]:
790 """Return a typed :class:`_sql.FromClause` from the give ORM model.
791
792 This function is just a typing help, at runtime it just returns the
793 ``__table__`` attribute of the provided ORM model.
794
795 It's usually called providing both the ORM model and the
796 :class:`_schema.TypedColumns` class. Single argument calls are supported
797 if the ORM model class provides an annotation pointing to its
798 :class:`_schema.TypedColumns` in the ``__typed_cols__`` attribute.
799
800
801 Example usage::
802
803 from sqlalchemy import TypedColumns
804 from sqlalchemy.orm import DeclarativeBase, mapped_column
805 from sqlalchemy.orm import MappedColumn, as_typed_table
806
807
808 class Base(DeclarativeBase):
809 pass
810
811
812 class A(Base):
813 __tablename__ = "a"
814
815 id: MappedColumn[int] = mapped_column(primary_key=True)
816 data: MappedColumn[str]
817
818
819 class a_cols(A, TypedColumns):
820 pass
821
822
823 # table_a is annotated as FromClause[a_cols]
824 table_a = as_typed_table(A, a_cols)
825
826
827 class B(Base):
828 __tablename__ = "b"
829 __typed_cols__: "b_cols"
830
831 a: Mapped[int] = mapped_column(primary_key=True)
832 b: Mapped[str]
833
834
835 class b_cols(B, TypedColumns):
836 pass
837
838
839 # table_b is a FromClause[b_cols], can call with just B since it
840 # provides the __typed_cols__ annotation
841 table_b = as_typed_table(B)
842
843 For proper typing integration :class:`_orm.MappedColumn` should be used
844 to annotate the single columns, since it's a more specific annotation than
845 the usual :class:`_orm.Mapped` used for ORM attributes.
846
847 .. versionadded:: 2.1.0b2
848 """
849 return cls.__table__
850
851
852class DeclarativeBase(
853 # Inspectable is used only by the mypy plugin
854 inspection.Inspectable[InstanceState[Any]],
855 _DeclarativeTyping,
856 metaclass=DeclarativeAttributeIntercept,
857):
858 """Base class used for declarative class definitions.
859
860 The :class:`_orm.DeclarativeBase` allows for the creation of new
861 declarative bases in such a way that is compatible with type checkers::
862
863
864 from sqlalchemy.orm import DeclarativeBase
865
866
867 class Base(DeclarativeBase):
868 pass
869
870 The above ``Base`` class is now usable as the base for new declarative
871 mappings. The superclass makes use of the ``__init_subclass__()``
872 method to set up new classes and metaclasses aren't used.
873
874 When first used, the :class:`_orm.DeclarativeBase` class instantiates a new
875 :class:`_orm.registry` to be used with the base, assuming one was not
876 provided explicitly. The :class:`_orm.DeclarativeBase` class supports
877 class-level attributes which act as parameters for the construction of this
878 registry; such as to indicate a specific :class:`_schema.MetaData`
879 collection as well as a specific value for
880 :paramref:`_orm.registry.type_annotation_map`::
881
882 from typing import Annotated
883
884 from sqlalchemy import BigInteger
885 from sqlalchemy import MetaData
886 from sqlalchemy import String
887 from sqlalchemy.orm import DeclarativeBase
888
889 bigint = Annotated[int, "bigint"]
890 my_metadata = MetaData()
891
892
893 class Base(DeclarativeBase):
894 metadata = my_metadata
895 type_annotation_map = {
896 str: String().with_variant(String(255), "mysql", "mariadb"),
897 bigint: BigInteger(),
898 }
899
900 Class-level attributes which may be specified include:
901
902 :param metadata: optional :class:`_schema.MetaData` collection.
903 If a :class:`_orm.registry` is constructed automatically, this
904 :class:`_schema.MetaData` collection will be used to construct it.
905 Otherwise, the local :class:`_schema.MetaData` collection will supersede
906 that used by an existing :class:`_orm.registry` passed using the
907 :paramref:`_orm.DeclarativeBase.registry` parameter.
908 :param type_annotation_map: optional type annotation map that will be
909 passed to the :class:`_orm.registry` as
910 :paramref:`_orm.registry.type_annotation_map`.
911 :param registry: supply a pre-existing :class:`_orm.registry` directly.
912
913 .. versionadded:: 2.0 Added :class:`.DeclarativeBase`, so that declarative
914 base classes may be constructed in such a way that is also recognized
915 by :pep:`484` type checkers. As a result, :class:`.DeclarativeBase`
916 and other subclassing-oriented APIs should be seen as
917 superseding previous "class returned by a function" APIs, namely
918 :func:`_orm.declarative_base` and :meth:`_orm.registry.generate_base`,
919 where the base class returned cannot be recognized by type checkers
920 without using plugins.
921
922 **__init__ behavior**
923
924 In a plain Python class, the base-most ``__init__()`` method in the class
925 hierarchy is ``object.__init__()``, which accepts no arguments. However,
926 when the :class:`_orm.DeclarativeBase` subclass is first declared, the
927 class is given an ``__init__()`` method that links to the
928 :paramref:`_orm.registry.constructor` constructor function, if no
929 ``__init__()`` method is already present; this is the usual declarative
930 constructor that will assign keyword arguments as attributes on the
931 instance, assuming those attributes are established at the class level
932 (i.e. are mapped, or are linked to a descriptor). This constructor is
933 **never accessed by a mapped class without being called explicitly via
934 super()**, as mapped classes are themselves given an ``__init__()`` method
935 directly which calls :paramref:`_orm.registry.constructor`, so in the
936 default case works independently of what the base-most ``__init__()``
937 method does.
938
939 .. versionchanged:: 2.0.1 :class:`_orm.DeclarativeBase` has a default
940 constructor that links to :paramref:`_orm.registry.constructor` by
941 default, so that calls to ``super().__init__()`` can access this
942 constructor. Previously, due to an implementation mistake, this default
943 constructor was missing, and calling ``super().__init__()`` would invoke
944 ``object.__init__()``.
945
946 The :class:`_orm.DeclarativeBase` subclass may also declare an explicit
947 ``__init__()`` method which will replace the use of the
948 :paramref:`_orm.registry.constructor` function at this level::
949
950 class Base(DeclarativeBase):
951 def __init__(self, id=None):
952 self.id = id
953
954 Mapped classes still will not invoke this constructor implicitly; it
955 remains only accessible by calling ``super().__init__()``::
956
957 class MyClass(Base):
958 def __init__(self, id=None, name=None):
959 self.name = name
960 super().__init__(id=id)
961
962 Note that this is a different behavior from what functions like the legacy
963 :func:`_orm.declarative_base` would do; the base created by those functions
964 would always install :paramref:`_orm.registry.constructor` for
965 ``__init__()``.
966
967
968 """
969
970 def __init_subclass__(cls, **kw: Any) -> None:
971 if DeclarativeBase in cls.__bases__:
972 _check_not_declarative(cls, DeclarativeBase)
973 _setup_declarative_base(cls)
974 else:
975 _ORMClassConfigurator._as_declarative(
976 cls._sa_registry, cls, cls.__dict__
977 )
978 super().__init_subclass__(**kw)
979
980
981def _check_not_declarative(cls: Type[Any], base: Type[Any]) -> None:
982 cls_dict = cls.__dict__
983 if (
984 "__table__" in cls_dict
985 and not (
986 callable(cls_dict["__table__"])
987 or hasattr(cls_dict["__table__"], "__get__")
988 )
989 ) or isinstance(cls_dict.get("__tablename__", None), str):
990 raise exc.InvalidRequestError(
991 f"Cannot use {base.__name__!r} directly as a declarative base "
992 "class. Create a Base by creating a subclass of it."
993 )
994
995
996class DeclarativeBaseNoMeta(
997 # Inspectable is used only by the mypy plugin
998 inspection.Inspectable[InstanceState[Any]],
999 _DeclarativeTyping,
1000):
1001 """Same as :class:`_orm.DeclarativeBase`, but does not use a metaclass
1002 to intercept new attributes.
1003
1004 The :class:`_orm.DeclarativeBaseNoMeta` base may be used when use of
1005 custom metaclasses is desirable.
1006
1007 .. versionadded:: 2.0
1008
1009
1010 """
1011
1012 def __init_subclass__(cls, **kw: Any) -> None:
1013 if DeclarativeBaseNoMeta in cls.__bases__:
1014 _check_not_declarative(cls, DeclarativeBaseNoMeta)
1015 _setup_declarative_base(cls)
1016 else:
1017 _ORMClassConfigurator._as_declarative(
1018 cls._sa_registry, cls, cls.__dict__
1019 )
1020 super().__init_subclass__(**kw)
1021
1022
1023def add_mapped_attribute(
1024 target: Type[_O], key: str, attr: MapperProperty[Any]
1025) -> None:
1026 """Add a new mapped attribute to an ORM mapped class.
1027
1028 E.g.::
1029
1030 add_mapped_attribute(User, "addresses", relationship(Address))
1031
1032 This may be used for ORM mappings that aren't using a declarative
1033 metaclass that intercepts attribute set operations.
1034
1035 .. versionadded:: 2.0
1036
1037
1038 """
1039 _add_attribute(target, key, attr)
1040
1041
1042def declarative_base(
1043 *,
1044 metadata: Optional[MetaData] = None,
1045 mapper: Optional[Callable[..., Mapper[Any]]] = None,
1046 cls: Type[Any] = object,
1047 name: str = "Base",
1048 class_registry: Optional[clsregistry._ClsRegistryType] = None,
1049 type_annotation_map: Optional[_TypeAnnotationMapType] = None,
1050 constructor: Callable[..., None] = _declarative_constructor,
1051 metaclass: Type[Any] = DeclarativeMeta,
1052) -> Any:
1053 r"""Construct a base class for declarative class definitions.
1054
1055 The new base class will be given a metaclass that produces
1056 appropriate :class:`~sqlalchemy.schema.Table` objects and makes
1057 the appropriate :class:`_orm.Mapper` calls based on the
1058 information provided declaratively in the class and any subclasses
1059 of the class.
1060
1061 .. versionchanged:: 2.0 Note that the :func:`_orm.declarative_base`
1062 function is superseded by the new :class:`_orm.DeclarativeBase` class,
1063 which generates a new "base" class using subclassing, rather than
1064 return value of a function. This allows an approach that is compatible
1065 with :pep:`484` typing tools.
1066
1067 The :func:`_orm.declarative_base` function is a shorthand version
1068 of using the :meth:`_orm.registry.generate_base`
1069 method. That is, the following::
1070
1071 from sqlalchemy.orm import declarative_base
1072
1073 Base = declarative_base()
1074
1075 Is equivalent to::
1076
1077 from sqlalchemy.orm import registry
1078
1079 mapper_registry = registry()
1080 Base = mapper_registry.generate_base()
1081
1082 See the docstring for :class:`_orm.registry`
1083 and :meth:`_orm.registry.generate_base`
1084 for more details.
1085
1086 .. versionchanged:: 1.4 The :func:`_orm.declarative_base`
1087 function is now a specialization of the more generic
1088 :class:`_orm.registry` class. The function also moves to the
1089 ``sqlalchemy.orm`` package from the ``declarative.ext`` package.
1090
1091
1092 :param metadata:
1093 An optional :class:`~sqlalchemy.schema.MetaData` instance. All
1094 :class:`~sqlalchemy.schema.Table` objects implicitly declared by
1095 subclasses of the base will share this MetaData. A MetaData instance
1096 will be created if none is provided. The
1097 :class:`~sqlalchemy.schema.MetaData` instance will be available via the
1098 ``metadata`` attribute of the generated declarative base class.
1099
1100 :param mapper:
1101 An optional callable, defaults to :class:`_orm.Mapper`. Will
1102 be used to map subclasses to their Tables.
1103
1104 :param cls:
1105 Defaults to :class:`object`. A type to use as the base for the generated
1106 declarative base class. May be a class or tuple of classes.
1107
1108 :param name:
1109 Defaults to ``Base``. The display name for the generated
1110 class. Customizing this is not required, but can improve clarity in
1111 tracebacks and debugging.
1112
1113 :param constructor:
1114 Specify the implementation for the ``__init__`` function on a mapped
1115 class that has no ``__init__`` of its own. Defaults to an
1116 implementation that assigns \**kwargs for declared
1117 fields and relationships to an instance. If ``None`` is supplied,
1118 no __init__ will be provided and construction will fall back to
1119 cls.__init__ by way of the normal Python semantics.
1120
1121 :param class_registry: optional dictionary that will serve as the
1122 registry of class names-> mapped classes when string names
1123 are used to identify classes inside of :func:`_orm.relationship`
1124 and others. Allows two or more declarative base classes
1125 to share the same registry of class names for simplified
1126 inter-base relationships.
1127
1128 :param type_annotation_map: optional dictionary of Python types to
1129 SQLAlchemy :class:`_types.TypeEngine` classes or instances. This
1130 is used exclusively by the :class:`_orm.MappedColumn` construct
1131 to produce column types based on annotations within the
1132 :class:`_orm.Mapped` type.
1133
1134
1135 .. versionadded:: 2.0
1136
1137 .. seealso::
1138
1139 :ref:`orm_declarative_mapped_column_type_map`
1140
1141 :param metaclass:
1142 Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__
1143 compatible callable to use as the meta type of the generated
1144 declarative base class.
1145
1146 .. seealso::
1147
1148 :class:`_orm.registry`
1149
1150 """
1151
1152 return registry(
1153 metadata=metadata,
1154 class_registry=class_registry,
1155 constructor=constructor,
1156 type_annotation_map=type_annotation_map,
1157 ).generate_base(
1158 mapper=mapper,
1159 cls=cls,
1160 name=name,
1161 metaclass=metaclass,
1162 )
1163
1164
1165class registry(EventTarget):
1166 """Generalized registry for mapping classes.
1167
1168 The :class:`_orm.registry` serves as the basis for maintaining a collection
1169 of mappings, and provides configurational hooks used to map classes.
1170
1171 The three general kinds of mappings supported are Declarative Base,
1172 Declarative Decorator, and Imperative Mapping. All of these mapping
1173 styles may be used interchangeably:
1174
1175 * :meth:`_orm.registry.generate_base` returns a new declarative base
1176 class, and is the underlying implementation of the
1177 :func:`_orm.declarative_base` function.
1178
1179 * :meth:`_orm.registry.mapped` provides a class decorator that will
1180 apply declarative mapping to a class without the use of a declarative
1181 base class.
1182
1183 * :meth:`_orm.registry.map_imperatively` will produce a
1184 :class:`_orm.Mapper` for a class without scanning the class for
1185 declarative class attributes. This method suits the use case historically
1186 provided by the ``sqlalchemy.orm.mapper()`` classical mapping function,
1187 which is removed as of SQLAlchemy 2.0.
1188
1189 .. versionadded:: 1.4
1190
1191 .. seealso::
1192
1193 :ref:`orm_mapping_classes_toplevel` - overview of class mapping
1194 styles.
1195
1196 """
1197
1198 _class_registry: clsregistry._ClsRegistryType
1199 _managers: weakref.WeakKeyDictionary[ClassManager[Any], Literal[True]]
1200 metadata: MetaData
1201 constructor: CallableReference[Callable[..., None]]
1202 type_annotation_map: _MutableTypeAnnotationMapType
1203 _dependents: Set[_RegistryType]
1204 _dependencies: Set[_RegistryType]
1205 _new_mappers: bool
1206 dispatch: dispatcher["registry"]
1207
1208 def __init__(
1209 self,
1210 *,
1211 metadata: Optional[MetaData] = None,
1212 class_registry: Optional[clsregistry._ClsRegistryType] = None,
1213 type_annotation_map: Optional[_TypeAnnotationMapType] = None,
1214 constructor: Callable[..., None] = _declarative_constructor,
1215 ):
1216 r"""Construct a new :class:`_orm.registry`
1217
1218 :param metadata:
1219 An optional :class:`_schema.MetaData` instance. All
1220 :class:`_schema.Table` objects generated using declarative
1221 table mapping will make use of this :class:`_schema.MetaData`
1222 collection. If this argument is left at its default of ``None``,
1223 a blank :class:`_schema.MetaData` collection is created.
1224
1225 :param constructor:
1226 Specify the implementation for the ``__init__`` function on a mapped
1227 class that has no ``__init__`` of its own. Defaults to an
1228 implementation that assigns \**kwargs for declared
1229 fields and relationships to an instance. If ``None`` is supplied,
1230 no __init__ will be provided and construction will fall back to
1231 cls.__init__ by way of the normal Python semantics.
1232
1233 :param class_registry: optional dictionary that will serve as the
1234 registry of class names-> mapped classes when string names
1235 are used to identify classes inside of :func:`_orm.relationship`
1236 and others. Allows two or more declarative base classes
1237 to share the same registry of class names for simplified
1238 inter-base relationships.
1239
1240 :param type_annotation_map: optional dictionary of Python types to
1241 SQLAlchemy :class:`_types.TypeEngine` classes or instances.
1242 The provided dict will update the default type mapping. This
1243 is used exclusively by the :class:`_orm.MappedColumn` construct
1244 to produce column types based on annotations within the
1245 :class:`_orm.Mapped` type.
1246
1247 .. versionadded:: 2.0
1248
1249 .. seealso::
1250
1251 :ref:`orm_declarative_mapped_column_type_map`
1252
1253
1254 """
1255 lcl_metadata = metadata or MetaData()
1256
1257 if class_registry is None:
1258 class_registry = weakref.WeakValueDictionary()
1259
1260 self._class_registry = class_registry
1261 self._managers = weakref.WeakKeyDictionary()
1262 self.metadata = lcl_metadata
1263 self.constructor = constructor
1264 self.type_annotation_map = {}
1265 if type_annotation_map is not None:
1266 self.update_type_annotation_map(type_annotation_map)
1267 self._dependents = set()
1268 self._dependencies = set()
1269
1270 self._new_mappers = False
1271
1272 with mapperlib._CONFIGURE_MUTEX:
1273 mapperlib._mapper_registries[self] = True
1274
1275 def update_type_annotation_map(
1276 self,
1277 type_annotation_map: _TypeAnnotationMapType,
1278 ) -> None:
1279 """update the :paramref:`_orm.registry.type_annotation_map` with new
1280 values."""
1281
1282 self.type_annotation_map.update(
1283 {
1284 de_optionalize_union_types(typ): sqltype
1285 for typ, sqltype in type_annotation_map.items()
1286 }
1287 )
1288
1289 def _resolve_type_with_events(
1290 self,
1291 cls: Any,
1292 key: str,
1293 raw_annotation: _MatchedOnType,
1294 extracted_type: _MatchedOnType,
1295 *,
1296 raw_pep_593_type: Optional[GenericProtocol[Any]] = None,
1297 pep_593_resolved_argument: Optional[_MatchedOnType] = None,
1298 raw_pep_695_type: Optional[TypeAliasType] = None,
1299 pep_695_resolved_value: Optional[_MatchedOnType] = None,
1300 ) -> Optional[sqltypes.TypeEngine[Any]]:
1301 """Resolve type with event support for custom type mapping.
1302
1303 This method fires the resolve_type_annotation event first to allow
1304 custom resolution, then falls back to normal resolution.
1305
1306 """
1307
1308 if self.dispatch.resolve_type_annotation:
1309 type_resolve = TypeResolve(
1310 self,
1311 cls,
1312 key,
1313 raw_annotation,
1314 (
1315 pep_593_resolved_argument
1316 if pep_593_resolved_argument is not None
1317 else (
1318 pep_695_resolved_value
1319 if pep_695_resolved_value is not None
1320 else extracted_type
1321 )
1322 ),
1323 raw_pep_593_type,
1324 pep_593_resolved_argument,
1325 raw_pep_695_type,
1326 pep_695_resolved_value,
1327 )
1328
1329 for fn in self.dispatch.resolve_type_annotation:
1330 result = fn(type_resolve)
1331 if result is not None:
1332 return sqltypes.to_instance(result) # type: ignore[no-any-return] # noqa: E501
1333
1334 if raw_pep_695_type is not None:
1335 sqltype = self._resolve_type(raw_pep_695_type)
1336 if sqltype is not None:
1337 return sqltype
1338
1339 sqltype = self._resolve_type(extracted_type)
1340 if sqltype is not None:
1341 return sqltype
1342
1343 if pep_593_resolved_argument is not None:
1344 sqltype = self._resolve_type(pep_593_resolved_argument)
1345
1346 return sqltype
1347
1348 def _resolve_type(
1349 self, python_type: _MatchedOnType
1350 ) -> Optional[sqltypes.TypeEngine[Any]]:
1351 python_type_type: Type[Any]
1352 search: Iterable[Tuple[_MatchedOnType, Type[Any]]]
1353
1354 if is_generic(python_type):
1355 if is_literal(python_type):
1356 python_type_type = python_type # type: ignore[assignment]
1357
1358 search = (
1359 (python_type, python_type_type),
1360 *((lt, python_type_type) for lt in LITERAL_TYPES),
1361 )
1362 else:
1363 python_type_type = python_type.__origin__
1364 search = ((python_type, python_type_type),)
1365 elif isinstance(python_type, type):
1366 python_type_type = python_type
1367 search = ((pt, pt) for pt in python_type_type.__mro__)
1368 else:
1369 python_type_type = python_type # type: ignore[assignment]
1370 search = ((python_type, python_type_type),)
1371
1372 for pt, flattened in search:
1373 # we search through full __mro__ for types. however...
1374 sql_type = self.type_annotation_map.get(pt)
1375 if sql_type is None:
1376 sql_type = sqltypes._type_map_get(pt) # type: ignore # noqa: E501
1377
1378 if sql_type is not None:
1379 sql_type_inst = sqltypes.to_instance(sql_type)
1380
1381 # ... this additional step will reject most
1382 # type -> supertype matches, such as if we had
1383 # a MyInt(int) subclass. note also we pass NewType()
1384 # here directly; these always have to be in the
1385 # type_annotation_map to be useful
1386 resolved_sql_type = sql_type_inst._resolve_for_python_type(
1387 python_type_type,
1388 pt,
1389 flattened,
1390 )
1391 if resolved_sql_type is not None:
1392 return resolved_sql_type
1393
1394 return None
1395
1396 @property
1397 def mappers(self) -> FrozenSet[Mapper[Any]]:
1398 """read only collection of all :class:`_orm.Mapper` objects."""
1399
1400 return frozenset(manager.mapper for manager in self._managers)
1401
1402 def _set_depends_on(self, registry: RegistryType) -> None:
1403 if registry is self:
1404 return
1405 registry._dependents.add(self)
1406 self._dependencies.add(registry)
1407
1408 def _flag_new_mapper(self, mapper: Mapper[Any]) -> None:
1409 mapper._ready_for_configure = True
1410 if self._new_mappers:
1411 return
1412
1413 for reg in self._recurse_with_dependents({self}):
1414 reg._new_mappers = True
1415
1416 @classmethod
1417 def _recurse_with_dependents(
1418 cls, registries: Set[RegistryType]
1419 ) -> Iterator[RegistryType]:
1420 todo = registries
1421 done = set()
1422 while todo:
1423 reg = todo.pop()
1424 done.add(reg)
1425
1426 # if yielding would remove dependents, make sure we have
1427 # them before
1428 todo.update(reg._dependents.difference(done))
1429 yield reg
1430
1431 # if yielding would add dependents, make sure we have them
1432 # after
1433 todo.update(reg._dependents.difference(done))
1434
1435 @classmethod
1436 def _recurse_with_dependencies(
1437 cls, registries: Set[RegistryType]
1438 ) -> Iterator[RegistryType]:
1439 todo = registries
1440 done = set()
1441 while todo:
1442 reg = todo.pop()
1443 done.add(reg)
1444
1445 # if yielding would remove dependencies, make sure we have
1446 # them before
1447 todo.update(reg._dependencies.difference(done))
1448
1449 yield reg
1450
1451 # if yielding would remove dependencies, make sure we have
1452 # them before
1453 todo.update(reg._dependencies.difference(done))
1454
1455 def _mappers_to_configure(self) -> Iterator[Mapper[Any]]:
1456 return (
1457 manager.mapper
1458 for manager in list(self._managers)
1459 if manager.is_mapped
1460 and not manager.mapper.configured
1461 and manager.mapper._ready_for_configure
1462 )
1463
1464 def _dispose_cls(self, cls: Type[_O]) -> None:
1465 clsregistry._remove_class(cls.__name__, cls, self._class_registry)
1466
1467 def _add_manager(self, manager: ClassManager[Any]) -> None:
1468 self._managers[manager] = True
1469 if manager.is_mapped:
1470 raise exc.ArgumentError(
1471 "Class '%s' already has a primary mapper defined. "
1472 % manager.class_
1473 )
1474 assert manager.registry is None
1475 manager.registry = self
1476
1477 def configure(self, cascade: bool = False) -> None:
1478 """Configure all as-yet unconfigured mappers in this
1479 :class:`_orm.registry`.
1480
1481 The configure step is used to reconcile and initialize the
1482 :func:`_orm.relationship` linkages between mapped classes, as well as
1483 to invoke configuration events such as the
1484 :meth:`_orm.MapperEvents.before_configured` and
1485 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM
1486 extensions or user-defined extension hooks.
1487
1488 If one or more mappers in this registry contain
1489 :func:`_orm.relationship` constructs that refer to mapped classes in
1490 other registries, this registry is said to be *dependent* on those
1491 registries. In order to configure those dependent registries
1492 automatically, the :paramref:`_orm.registry.configure.cascade` flag
1493 should be set to ``True``. Otherwise, if they are not configured, an
1494 exception will be raised. The rationale behind this behavior is to
1495 allow an application to programmatically invoke configuration of
1496 registries while controlling whether or not the process implicitly
1497 reaches other registries.
1498
1499 As an alternative to invoking :meth:`_orm.registry.configure`, the ORM
1500 function :func:`_orm.configure_mappers` function may be used to ensure
1501 configuration is complete for all :class:`_orm.registry` objects in
1502 memory. This is generally simpler to use and also predates the usage of
1503 :class:`_orm.registry` objects overall. However, this function will
1504 impact all mappings throughout the running Python process and may be
1505 more memory/time consuming for an application that has many registries
1506 in use for different purposes that may not be needed immediately.
1507
1508 .. seealso::
1509
1510 :func:`_orm.configure_mappers`
1511
1512
1513 .. versionadded:: 1.4.0b2
1514
1515 """
1516 mapperlib._configure_registries({self}, cascade=cascade)
1517
1518 def dispose(self, cascade: bool = False) -> None:
1519 """Dispose of all mappers in this :class:`_orm.registry`.
1520
1521 After invocation, all the classes that were mapped within this registry
1522 will no longer have class instrumentation associated with them. This
1523 method is the per-:class:`_orm.registry` analogue to the
1524 application-wide :func:`_orm.clear_mappers` function.
1525
1526 If this registry contains mappers that are dependencies of other
1527 registries, typically via :func:`_orm.relationship` links, then those
1528 registries must be disposed as well. When such registries exist in
1529 relation to this one, their :meth:`_orm.registry.dispose` method will
1530 also be called, if the :paramref:`_orm.registry.dispose.cascade` flag
1531 is set to ``True``; otherwise, an error is raised if those registries
1532 were not already disposed.
1533
1534 .. versionadded:: 1.4.0b2
1535
1536 .. seealso::
1537
1538 :func:`_orm.clear_mappers`
1539
1540 """
1541
1542 mapperlib._dispose_registries({self}, cascade=cascade)
1543
1544 def _dispose_manager_and_mapper(self, manager: ClassManager[Any]) -> None:
1545 if "mapper" in manager.__dict__:
1546 mapper = manager.mapper
1547
1548 mapper._set_dispose_flags()
1549
1550 class_ = manager.class_
1551 self._dispose_cls(class_)
1552 instrumentation._instrumentation_factory.unregister(class_)
1553
1554 def generate_base(
1555 self,
1556 mapper: Optional[Callable[..., Mapper[Any]]] = None,
1557 cls: Type[Any] = object,
1558 name: str = "Base",
1559 metaclass: Type[Any] = DeclarativeMeta,
1560 ) -> Any:
1561 """Generate a declarative base class.
1562
1563 Classes that inherit from the returned class object will be
1564 automatically mapped using declarative mapping.
1565
1566 E.g.::
1567
1568 from sqlalchemy.orm import registry
1569
1570 mapper_registry = registry()
1571
1572 Base = mapper_registry.generate_base()
1573
1574
1575 class MyClass(Base):
1576 __tablename__ = "my_table"
1577 id = Column(Integer, primary_key=True)
1578
1579 The above dynamically generated class is equivalent to the
1580 non-dynamic example below::
1581
1582 from sqlalchemy.orm import registry
1583 from sqlalchemy.orm.decl_api import DeclarativeMeta
1584
1585 mapper_registry = registry()
1586
1587
1588 class Base(metaclass=DeclarativeMeta):
1589 __abstract__ = True
1590 registry = mapper_registry
1591 metadata = mapper_registry.metadata
1592
1593 __init__ = mapper_registry.constructor
1594
1595 .. versionchanged:: 2.0 Note that the
1596 :meth:`_orm.registry.generate_base` method is superseded by the new
1597 :class:`_orm.DeclarativeBase` class, which generates a new "base"
1598 class using subclassing, rather than return value of a function.
1599 This allows an approach that is compatible with :pep:`484` typing
1600 tools.
1601
1602 The :meth:`_orm.registry.generate_base` method provides the
1603 implementation for the :func:`_orm.declarative_base` function, which
1604 creates the :class:`_orm.registry` and base class all at once.
1605
1606 See the section :ref:`orm_declarative_mapping` for background and
1607 examples.
1608
1609 :param mapper:
1610 An optional callable, defaults to :class:`_orm.Mapper`.
1611 This function is used to generate new :class:`_orm.Mapper` objects.
1612
1613 :param cls:
1614 Defaults to :class:`object`. A type to use as the base for the
1615 generated declarative base class. May be a class or tuple of classes.
1616
1617 :param name:
1618 Defaults to ``Base``. The display name for the generated
1619 class. Customizing this is not required, but can improve clarity in
1620 tracebacks and debugging.
1621
1622 :param metaclass:
1623 Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__
1624 compatible callable to use as the meta type of the generated
1625 declarative base class.
1626
1627 .. seealso::
1628
1629 :ref:`orm_declarative_mapping`
1630
1631 :func:`_orm.declarative_base`
1632
1633 """
1634 metadata = self.metadata
1635
1636 bases = not isinstance(cls, tuple) and (cls,) or cls
1637
1638 class_dict: Dict[str, Any] = dict(registry=self, metadata=metadata)
1639 if isinstance(cls, type):
1640 class_dict["__doc__"] = cls.__doc__
1641
1642 if self.constructor is not None:
1643 class_dict["__init__"] = self.constructor
1644
1645 class_dict["__abstract__"] = True
1646 if mapper:
1647 class_dict["__mapper_cls__"] = mapper
1648
1649 if hasattr(cls, "__class_getitem__"):
1650
1651 def __class_getitem__(cls: Type[_T], key: Any) -> Type[_T]:
1652 # allow generic classes in py3.9+
1653 return cls
1654
1655 class_dict["__class_getitem__"] = __class_getitem__
1656
1657 return metaclass(name, bases, class_dict)
1658
1659 @compat_typing.dataclass_transform(
1660 field_specifiers=(
1661 MappedColumn,
1662 RelationshipProperty,
1663 Composite,
1664 Synonym,
1665 mapped_column,
1666 relationship,
1667 composite,
1668 synonym,
1669 deferred,
1670 ),
1671 )
1672 @overload
1673 def mapped_as_dataclass(self, __cls: Type[_O], /) -> Type[_O]: ...
1674
1675 @overload
1676 def mapped_as_dataclass(
1677 self,
1678 __cls: Literal[None] = ...,
1679 /,
1680 *,
1681 init: Union[_NoArg, bool] = ...,
1682 repr: Union[_NoArg, bool] = ..., # noqa: A002
1683 eq: Union[_NoArg, bool] = ...,
1684 order: Union[_NoArg, bool] = ...,
1685 unsafe_hash: Union[_NoArg, bool] = ...,
1686 match_args: Union[_NoArg, bool] = ...,
1687 kw_only: Union[_NoArg, bool] = ...,
1688 dataclass_callable: Union[_NoArg, Callable[..., Type[Any]]] = ...,
1689 ) -> Callable[[Type[_O]], Type[_O]]: ...
1690
1691 def mapped_as_dataclass(
1692 self,
1693 __cls: Optional[Type[_O]] = None,
1694 /,
1695 *,
1696 init: Union[_NoArg, bool] = _NoArg.NO_ARG,
1697 repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
1698 eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
1699 order: Union[_NoArg, bool] = _NoArg.NO_ARG,
1700 unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
1701 match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
1702 kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
1703 dataclass_callable: Union[
1704 _NoArg, Callable[..., Type[Any]]
1705 ] = _NoArg.NO_ARG,
1706 ) -> Union[Type[_O], Callable[[Type[_O]], Type[_O]]]:
1707 """Class decorator that will apply the Declarative mapping process
1708 to a given class, and additionally convert the class to be a
1709 Python dataclass.
1710
1711 .. seealso::
1712
1713 :ref:`orm_declarative_native_dataclasses` - complete background
1714 on SQLAlchemy native dataclass mapping
1715
1716 :func:`_orm.mapped_as_dataclass` - functional version that may
1717 provide better compatibility with mypy
1718
1719 .. versionadded:: 2.0
1720
1721
1722 """
1723
1724 decorate = mapped_as_dataclass(
1725 self,
1726 init=init,
1727 repr=repr,
1728 eq=eq,
1729 order=order,
1730 unsafe_hash=unsafe_hash,
1731 match_args=match_args,
1732 kw_only=kw_only,
1733 dataclass_callable=dataclass_callable,
1734 )
1735
1736 if __cls:
1737 return decorate(__cls)
1738 else:
1739 return decorate
1740
1741 def mapped(self, cls: Type[_O]) -> Type[_O]:
1742 """Class decorator that will apply the Declarative mapping process
1743 to a given class.
1744
1745 E.g.::
1746
1747 from sqlalchemy.orm import registry
1748
1749 mapper_registry = registry()
1750
1751
1752 @mapper_registry.mapped
1753 class Foo:
1754 __tablename__ = "some_table"
1755
1756 id = Column(Integer, primary_key=True)
1757 name = Column(String)
1758
1759 See the section :ref:`orm_declarative_mapping` for complete
1760 details and examples.
1761
1762 :param cls: class to be mapped.
1763
1764 :return: the class that was passed.
1765
1766 .. seealso::
1767
1768 :ref:`orm_declarative_mapping`
1769
1770 :meth:`_orm.registry.generate_base` - generates a base class
1771 that will apply Declarative mapping to subclasses automatically
1772 using a Python metaclass.
1773
1774 .. seealso::
1775
1776 :meth:`_orm.registry.mapped_as_dataclass`
1777
1778 """
1779 _ORMClassConfigurator._as_declarative(self, cls, cls.__dict__)
1780 return cls
1781
1782 def as_declarative_base(self, **kw: Any) -> Callable[[Type[_T]], Type[_T]]:
1783 """
1784 Class decorator which will invoke
1785 :meth:`_orm.registry.generate_base`
1786 for a given base class.
1787
1788 E.g.::
1789
1790 from sqlalchemy.orm import registry
1791
1792 mapper_registry = registry()
1793
1794
1795 @mapper_registry.as_declarative_base()
1796 class Base:
1797 @declared_attr
1798 def __tablename__(cls):
1799 return cls.__name__.lower()
1800
1801 id = Column(Integer, primary_key=True)
1802
1803
1804 class MyMappedClass(Base): ...
1805
1806 All keyword arguments passed to
1807 :meth:`_orm.registry.as_declarative_base` are passed
1808 along to :meth:`_orm.registry.generate_base`.
1809
1810 """
1811
1812 def decorate(cls: Type[_T]) -> Type[_T]:
1813 kw["cls"] = cls
1814 kw["name"] = cls.__name__
1815 return self.generate_base(**kw) # type: ignore
1816
1817 return decorate
1818
1819 def map_declaratively(self, cls: Type[_O]) -> Mapper[_O]:
1820 """Map a class declaratively.
1821
1822 In this form of mapping, the class is scanned for mapping information,
1823 including for columns to be associated with a table, and/or an
1824 actual table object.
1825
1826 Returns the :class:`_orm.Mapper` object.
1827
1828 E.g.::
1829
1830 from sqlalchemy.orm import registry
1831
1832 mapper_registry = registry()
1833
1834
1835 class Foo:
1836 __tablename__ = "some_table"
1837
1838 id = Column(Integer, primary_key=True)
1839 name = Column(String)
1840
1841
1842 mapper = mapper_registry.map_declaratively(Foo)
1843
1844 This function is more conveniently invoked indirectly via either the
1845 :meth:`_orm.registry.mapped` class decorator or by subclassing a
1846 declarative metaclass generated from
1847 :meth:`_orm.registry.generate_base`.
1848
1849 See the section :ref:`orm_declarative_mapping` for complete
1850 details and examples.
1851
1852 :param cls: class to be mapped.
1853
1854 :return: a :class:`_orm.Mapper` object.
1855
1856 .. seealso::
1857
1858 :ref:`orm_declarative_mapping`
1859
1860 :meth:`_orm.registry.mapped` - more common decorator interface
1861 to this function.
1862
1863 :meth:`_orm.registry.map_imperatively`
1864
1865 """
1866 _ORMClassConfigurator._as_declarative(self, cls, cls.__dict__)
1867 return cls.__mapper__ # type: ignore
1868
1869 def map_imperatively(
1870 self,
1871 class_: Type[_O],
1872 local_table: Optional[FromClause] = None,
1873 **kw: Any,
1874 ) -> Mapper[_O]:
1875 r"""Map a class imperatively.
1876
1877 In this form of mapping, the class is not scanned for any mapping
1878 information. Instead, all mapping constructs are passed as
1879 arguments.
1880
1881 This method is intended to be fully equivalent to the now-removed
1882 SQLAlchemy ``mapper()`` function, except that it's in terms of
1883 a particular registry.
1884
1885 E.g.::
1886
1887 from sqlalchemy.orm import registry
1888
1889 mapper_registry = registry()
1890
1891 my_table = Table(
1892 "my_table",
1893 mapper_registry.metadata,
1894 Column("id", Integer, primary_key=True),
1895 )
1896
1897
1898 class MyClass:
1899 pass
1900
1901
1902 mapper_registry.map_imperatively(MyClass, my_table)
1903
1904 See the section :ref:`orm_imperative_mapping` for complete background
1905 and usage examples.
1906
1907 :param class\_: The class to be mapped. Corresponds to the
1908 :paramref:`_orm.Mapper.class_` parameter.
1909
1910 :param local_table: the :class:`_schema.Table` or other
1911 :class:`_sql.FromClause` object that is the subject of the mapping.
1912 Corresponds to the
1913 :paramref:`_orm.Mapper.local_table` parameter.
1914
1915 :param \**kw: all other keyword arguments are passed to the
1916 :class:`_orm.Mapper` constructor directly.
1917
1918 .. seealso::
1919
1920 :ref:`orm_imperative_mapping`
1921
1922 :ref:`orm_declarative_mapping`
1923
1924 """
1925 return _ORMClassConfigurator._mapper(self, class_, local_table, kw)
1926
1927
1928RegistryType = registry
1929
1930if not TYPE_CHECKING:
1931 # allow for runtime type resolution of ``ClassVar[_RegistryType]``
1932 _RegistryType = registry # noqa
1933
1934
1935class TypeResolve:
1936 """Primary argument to the :meth:`.RegistryEvents.resolve_type_annotation`
1937 event.
1938
1939 This object contains all the information needed to resolve a Python
1940 type to a SQLAlchemy type. The :attr:`.TypeResolve.resolved_type` is
1941 typically the main type that's resolved. To resolve an arbitrary
1942 Python type against the current type map, the :meth:`.TypeResolve.resolve`
1943 method may be used.
1944
1945 .. versionadded:: 2.1
1946
1947 """
1948
1949 __slots__ = (
1950 "registry",
1951 "cls",
1952 "key",
1953 "raw_type",
1954 "resolved_type",
1955 "raw_pep_593_type",
1956 "raw_pep_695_type",
1957 "pep_593_resolved_argument",
1958 "pep_695_resolved_value",
1959 )
1960
1961 cls: Any
1962 "The class being processed during declarative mapping"
1963
1964 registry: "registry"
1965 "The :class:`registry` being used"
1966
1967 key: str
1968 "String name of the ORM mapped attribute being processed"
1969
1970 raw_type: _MatchedOnType
1971 """The type annotation object directly from the attribute's annotations.
1972
1973 It's recommended to look at :attr:`.TypeResolve.resolved_type` or
1974 one of :attr:`.TypeResolve.pep_593_resolved_argument` or
1975 :attr:`.TypeResolve.pep_695_resolved_value` rather than the raw type, as
1976 the raw type will not be de-optionalized.
1977
1978 """
1979
1980 resolved_type: _MatchedOnType
1981 """The de-optionalized, "resolved" type after accounting for :pep:`695`
1982 and :pep:`593` indirection:
1983
1984 * If the annotation were a plain Python type or simple alias e.g.
1985 ``Mapped[int]``, the resolved_type will be ``int``
1986 * If the annotation refers to a :pep:`695` type that references a
1987 plain Python type or simple alias, e.g. ``type MyType = int``
1988 then ``Mapped[MyType]``, the type will refer to the ``__value__``
1989 of the :pep:`695` type, e.g. ``int``, the same as
1990 :attr:`.TypeResolve.pep_695_resolved_value`.
1991 * If the annotation refers to a :pep:`593` ``Annotated`` object, or
1992 a :pep:`695` type alias that in turn refers to a :pep:`593` type,
1993 then the type will be the inner type inside of the ``Annotated``,
1994 e.g. ``MyType = Annotated[float, mapped_column(...)]`` with
1995 ``Mapped[MyType]`` becomes ``float``, the same as
1996 :attr:`.TypeResolve.pep_593_resolved_argument`.
1997
1998 """
1999
2000 raw_pep_593_type: Optional[GenericProtocol[Any]]
2001 """The de-optionalized :pep:`593` type, if the raw type referred to one.
2002
2003 This would refer to an ``Annotated`` object.
2004
2005 """
2006
2007 pep_593_resolved_argument: Optional[_MatchedOnType]
2008 """The type extracted from a :pep:`593` ``Annotated`` construct, if the
2009 type referred to one.
2010
2011 When present, this type would be the same as the
2012 :attr:`.TypeResolve.resolved_type`.
2013
2014 """
2015
2016 raw_pep_695_type: Optional[TypeAliasType]
2017 "The de-optionalized :pep:`695` type, if the raw type referred to one."
2018
2019 pep_695_resolved_value: Optional[_MatchedOnType]
2020 """The de-optionalized type referenced by the raw :pep:`695` type, if the
2021 raw type referred to one.
2022
2023 When present, and a :pep:`593` type is not present, this type would be the
2024 same as the :attr:`.TypeResolve.resolved_type`.
2025
2026 """
2027
2028 def __init__(
2029 self,
2030 registry: RegistryType,
2031 cls: Any,
2032 key: str,
2033 raw_type: _MatchedOnType,
2034 resolved_type: _MatchedOnType,
2035 raw_pep_593_type: Optional[GenericProtocol[Any]],
2036 pep_593_resolved_argument: Optional[_MatchedOnType],
2037 raw_pep_695_type: Optional[TypeAliasType],
2038 pep_695_resolved_value: Optional[_MatchedOnType],
2039 ):
2040 self.registry = registry
2041 self.cls = cls
2042 self.key = key
2043 self.raw_type = raw_type
2044 self.resolved_type = resolved_type
2045 self.raw_pep_593_type = raw_pep_593_type
2046 self.pep_593_resolved_argument = pep_593_resolved_argument
2047 self.raw_pep_695_type = raw_pep_695_type
2048 self.pep_695_resolved_value = pep_695_resolved_value
2049
2050 def resolve(
2051 self, python_type: _MatchedOnType
2052 ) -> Optional[sqltypes.TypeEngine[Any]]:
2053 """Resolve the given python type using the type_annotation_map of
2054 the :class:`registry`.
2055
2056 :param python_type: a Python type (e.g. ``int``, ``str``, etc.) Any
2057 type object that's present in
2058 :paramref:`_orm.registry_type_annotation_map` should produce a
2059 non-``None`` result.
2060 :return: a SQLAlchemy :class:`.TypeEngine` instance
2061 (e.g. :class:`.Integer`,
2062 :class:`.String`, etc.), or ``None`` to indicate no type could be
2063 matched.
2064
2065 """
2066 return self.registry._resolve_type(python_type)
2067
2068
2069def as_declarative(**kw: Any) -> Callable[[Type[_T]], Type[_T]]:
2070 """
2071 Class decorator which will adapt a given class into a
2072 :func:`_orm.declarative_base`.
2073
2074 This function makes use of the :meth:`_orm.registry.as_declarative_base`
2075 method, by first creating a :class:`_orm.registry` automatically
2076 and then invoking the decorator.
2077
2078 E.g.::
2079
2080 from sqlalchemy.orm import as_declarative
2081
2082
2083 @as_declarative()
2084 class Base:
2085 @declared_attr
2086 def __tablename__(cls):
2087 return cls.__name__.lower()
2088
2089 id = Column(Integer, primary_key=True)
2090
2091
2092 class MyMappedClass(Base): ...
2093
2094 .. seealso::
2095
2096 :meth:`_orm.registry.as_declarative_base`
2097
2098 """
2099 metadata, class_registry = (
2100 kw.pop("metadata", None),
2101 kw.pop("class_registry", None),
2102 )
2103
2104 return registry(
2105 metadata=metadata, class_registry=class_registry
2106 ).as_declarative_base(**kw)
2107
2108
2109@compat_typing.dataclass_transform(
2110 field_specifiers=(
2111 MappedColumn,
2112 RelationshipProperty,
2113 Composite,
2114 Synonym,
2115 mapped_column,
2116 relationship,
2117 composite,
2118 synonym,
2119 deferred,
2120 ),
2121)
2122def mapped_as_dataclass(
2123 registry: RegistryType,
2124 /,
2125 *,
2126 init: Union[_NoArg, bool] = _NoArg.NO_ARG,
2127 repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
2128 eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
2129 order: Union[_NoArg, bool] = _NoArg.NO_ARG,
2130 unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
2131 match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
2132 kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
2133 dataclass_callable: Union[
2134 _NoArg, Callable[..., Type[Any]]
2135 ] = _NoArg.NO_ARG,
2136) -> Callable[[Type[_O]], Type[_O]]:
2137 """Standalone function form of :meth:`_orm.registry.mapped_as_dataclass`
2138 which may have better compatibility with mypy.
2139
2140 The :class:`_orm.registry` is passed as the first argument to the
2141 decorator.
2142
2143 e.g.::
2144
2145 from sqlalchemy.orm import Mapped
2146 from sqlalchemy.orm import mapped_as_dataclass
2147 from sqlalchemy.orm import mapped_column
2148 from sqlalchemy.orm import registry
2149
2150 some_registry = registry()
2151
2152
2153 @mapped_as_dataclass(some_registry)
2154 class Relationships:
2155 __tablename__ = "relationships"
2156
2157 entity_id1: Mapped[int] = mapped_column(primary_key=True)
2158 entity_id2: Mapped[int] = mapped_column(primary_key=True)
2159 level: Mapped[int] = mapped_column(Integer)
2160
2161 .. versionadded:: 2.0.44
2162
2163 """
2164
2165 def decorate(cls: Type[_O]) -> Type[_O]:
2166 _generate_dc_transforms(
2167 init=init,
2168 repr=repr,
2169 eq=eq,
2170 order=order,
2171 unsafe_hash=unsafe_hash,
2172 match_args=match_args,
2173 kw_only=kw_only,
2174 dataclass_callable=dataclass_callable,
2175 cls_=cls,
2176 )
2177 _ORMClassConfigurator._as_declarative(registry, cls, cls.__dict__)
2178 return cls
2179
2180 return decorate
2181
2182
2183@inspection._inspects(
2184 DeclarativeMeta, DeclarativeBase, DeclarativeAttributeIntercept
2185)
2186def _inspect_decl_meta(cls: Type[Any]) -> Optional[Mapper[Any]]:
2187 mp: Optional[Mapper[Any]] = _inspect_mapped_class(cls)
2188 if mp is None:
2189 if _DeferredDeclarativeConfig.has_cls(cls):
2190 _DeferredDeclarativeConfig.raise_unmapped_for_cls(cls)
2191 return mp
2192
2193
2194@compat_typing.dataclass_transform(
2195 field_specifiers=(
2196 MappedColumn,
2197 RelationshipProperty,
2198 Composite,
2199 Synonym,
2200 mapped_column,
2201 relationship,
2202 composite,
2203 synonym,
2204 deferred,
2205 ),
2206)
2207@overload
2208def unmapped_dataclass(__cls: Type[_O], /) -> Type[_O]: ...
2209
2210
2211@overload
2212def unmapped_dataclass(
2213 __cls: Literal[None] = ...,
2214 /,
2215 *,
2216 init: Union[_NoArg, bool] = ...,
2217 repr: Union[_NoArg, bool] = ..., # noqa: A002
2218 eq: Union[_NoArg, bool] = ...,
2219 order: Union[_NoArg, bool] = ...,
2220 unsafe_hash: Union[_NoArg, bool] = ...,
2221 match_args: Union[_NoArg, bool] = ...,
2222 kw_only: Union[_NoArg, bool] = ...,
2223 dataclass_callable: Union[_NoArg, Callable[..., Type[Any]]] = ...,
2224) -> Callable[[Type[_O]], Type[_O]]: ...
2225
2226
2227def unmapped_dataclass(
2228 __cls: Optional[Type[_O]] = None,
2229 /,
2230 *,
2231 init: Union[_NoArg, bool] = _NoArg.NO_ARG,
2232 repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
2233 eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
2234 order: Union[_NoArg, bool] = _NoArg.NO_ARG,
2235 unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
2236 match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
2237 kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
2238 dataclass_callable: Union[
2239 _NoArg, Callable[..., Type[Any]]
2240 ] = _NoArg.NO_ARG,
2241) -> Union[Type[_O], Callable[[Type[_O]], Type[_O]]]:
2242 """Decorator which allows the creation of dataclass-compatible mixins
2243 within mapped class hierarchies based on the
2244 :func:`_orm.mapped_as_dataclass` decorator.
2245
2246 Parameters are the same as those of :func:`_orm.mapped_as_dataclass`.
2247 The decorator turns the given class into a SQLAlchemy-compatible dataclass
2248 in the same way that :func:`_orm.mapped_as_dataclass` does, taking
2249 into account :func:`_orm.mapped_column` and other attributes for dataclass-
2250 specific directives, but not actually mapping the class.
2251
2252 To create unmapped dataclass mixins when using a class hierarchy defined
2253 by :class:`.DeclarativeBase` and :class:`.MappedAsDataclass`, the
2254 :class:`.MappedAsDataclass` class may be subclassed alone for a similar
2255 effect.
2256
2257 .. versionadded:: 2.1
2258
2259 .. seealso::
2260
2261 :ref:`orm_declarative_dc_mixins` - background and example use.
2262
2263 """
2264
2265 def decorate(cls: Type[_O]) -> Type[_O]:
2266 _generate_dc_transforms(
2267 init=init,
2268 repr=repr,
2269 eq=eq,
2270 order=order,
2271 unsafe_hash=unsafe_hash,
2272 match_args=match_args,
2273 kw_only=kw_only,
2274 dataclass_callable=dataclass_callable,
2275 cls_=cls,
2276 )
2277 _ORMClassConfigurator._as_unmapped_dataclass(cls, cls.__dict__)
2278 return cls
2279
2280 if __cls:
2281 return decorate(__cls)
2282 else:
2283 return decorate