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