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