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