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