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