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