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 .. legacy:: This api is considered legacy and will be deprecated in the next
516 SQLAlchemy version.
517
518 .. seealso::
519
520 :ref:`orm_mixins_toplevel`
521
522 :ref:`mypy_declarative_mixins` - in the
523 :ref:`Mypy plugin documentation <mypy_toplevel>`
524
525 """ # noqa: E501
526
527 return cls
528
529
530def _setup_declarative_base(cls: Type[Any]) -> None:
531 if "metadata" in cls.__dict__:
532 metadata = cls.__dict__["metadata"]
533 else:
534 metadata = None
535
536 if "type_annotation_map" in cls.__dict__:
537 type_annotation_map = cls.__dict__["type_annotation_map"]
538 else:
539 type_annotation_map = None
540
541 reg = cls.__dict__.get("registry", None)
542 if reg is not None:
543 if not isinstance(reg, registry):
544 raise exc.InvalidRequestError(
545 "Declarative base class has a 'registry' attribute that is "
546 "not an instance of sqlalchemy.orm.registry()"
547 )
548 elif type_annotation_map is not None:
549 raise exc.InvalidRequestError(
550 "Declarative base class has both a 'registry' attribute and a "
551 "type_annotation_map entry. Per-base type_annotation_maps "
552 "are not supported. Please apply the type_annotation_map "
553 "to this registry directly."
554 )
555
556 else:
557 reg = registry(
558 metadata=metadata, type_annotation_map=type_annotation_map
559 )
560 cls.registry = reg
561
562 cls._sa_registry = reg
563
564 if "metadata" not in cls.__dict__:
565 cls.metadata = cls.registry.metadata
566
567 if getattr(cls, "__init__", object.__init__) is object.__init__:
568 cls.__init__ = cls.registry.constructor
569
570
571class MappedAsDataclass(metaclass=DCTransformDeclarative):
572 """Mixin class to indicate when mapping this class, also convert it to be
573 a dataclass.
574
575 .. seealso::
576
577 :ref:`orm_declarative_native_dataclasses` - complete background
578 on SQLAlchemy native dataclass mapping
579
580 .. versionadded:: 2.0
581
582 """
583
584 def __init_subclass__(
585 cls,
586 init: Union[_NoArg, bool] = _NoArg.NO_ARG,
587 repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
588 eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
589 order: Union[_NoArg, bool] = _NoArg.NO_ARG,
590 unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
591 match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
592 kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
593 dataclass_callable: Union[
594 _NoArg, Callable[..., Type[Any]]
595 ] = _NoArg.NO_ARG,
596 **kw: Any,
597 ) -> None:
598 apply_dc_transforms: _DataclassArguments = {
599 "init": init,
600 "repr": repr,
601 "eq": eq,
602 "order": order,
603 "unsafe_hash": unsafe_hash,
604 "match_args": match_args,
605 "kw_only": kw_only,
606 "dataclass_callable": dataclass_callable,
607 }
608
609 current_transforms: _DataclassArguments
610
611 if hasattr(cls, "_sa_apply_dc_transforms"):
612 current = cls._sa_apply_dc_transforms
613
614 _ClassScanMapperConfig._assert_dc_arguments(current)
615
616 cls._sa_apply_dc_transforms = current_transforms = { # type: ignore # noqa: E501
617 k: current.get(k, _NoArg.NO_ARG) if v is _NoArg.NO_ARG else v
618 for k, v in apply_dc_transforms.items()
619 }
620 else:
621 cls._sa_apply_dc_transforms = current_transforms = (
622 apply_dc_transforms
623 )
624
625 super().__init_subclass__(**kw)
626
627 if not _is_mapped_class(cls):
628 new_anno = (
629 _ClassScanMapperConfig._update_annotations_for_non_mapped_class
630 )(cls)
631 _ClassScanMapperConfig._apply_dataclasses_to_any_class(
632 current_transforms, cls, new_anno
633 )
634
635
636class DeclarativeBase(
637 # Inspectable is used only by the mypy plugin
638 inspection.Inspectable[InstanceState[Any]],
639 metaclass=DeclarativeAttributeIntercept,
640):
641 """Base class used for declarative class definitions.
642
643 The :class:`_orm.DeclarativeBase` allows for the creation of new
644 declarative bases in such a way that is compatible with type checkers::
645
646
647 from sqlalchemy.orm import DeclarativeBase
648
649
650 class Base(DeclarativeBase):
651 pass
652
653 The above ``Base`` class is now usable as the base for new declarative
654 mappings. The superclass makes use of the ``__init_subclass__()``
655 method to set up new classes and metaclasses aren't used.
656
657 When first used, the :class:`_orm.DeclarativeBase` class instantiates a new
658 :class:`_orm.registry` to be used with the base, assuming one was not
659 provided explicitly. The :class:`_orm.DeclarativeBase` class supports
660 class-level attributes which act as parameters for the construction of this
661 registry; such as to indicate a specific :class:`_schema.MetaData`
662 collection as well as a specific value for
663 :paramref:`_orm.registry.type_annotation_map`::
664
665 from typing_extensions import Annotated
666
667 from sqlalchemy import BigInteger
668 from sqlalchemy import MetaData
669 from sqlalchemy import String
670 from sqlalchemy.orm import DeclarativeBase
671
672 bigint = Annotated[int, "bigint"]
673 my_metadata = MetaData()
674
675
676 class Base(DeclarativeBase):
677 metadata = my_metadata
678 type_annotation_map = {
679 str: String().with_variant(String(255), "mysql", "mariadb"),
680 bigint: BigInteger(),
681 }
682
683 Class-level attributes which may be specified include:
684
685 :param metadata: optional :class:`_schema.MetaData` collection.
686 If a :class:`_orm.registry` is constructed automatically, this
687 :class:`_schema.MetaData` collection will be used to construct it.
688 Otherwise, the local :class:`_schema.MetaData` collection will supercede
689 that used by an existing :class:`_orm.registry` passed using the
690 :paramref:`_orm.DeclarativeBase.registry` parameter.
691 :param type_annotation_map: optional type annotation map that will be
692 passed to the :class:`_orm.registry` as
693 :paramref:`_orm.registry.type_annotation_map`.
694 :param registry: supply a pre-existing :class:`_orm.registry` directly.
695
696 .. versionadded:: 2.0 Added :class:`.DeclarativeBase`, so that declarative
697 base classes may be constructed in such a way that is also recognized
698 by :pep:`484` type checkers. As a result, :class:`.DeclarativeBase`
699 and other subclassing-oriented APIs should be seen as
700 superseding previous "class returned by a function" APIs, namely
701 :func:`_orm.declarative_base` and :meth:`_orm.registry.generate_base`,
702 where the base class returned cannot be recognized by type checkers
703 without using plugins.
704
705 **__init__ behavior**
706
707 In a plain Python class, the base-most ``__init__()`` method in the class
708 hierarchy is ``object.__init__()``, which accepts no arguments. However,
709 when the :class:`_orm.DeclarativeBase` subclass is first declared, the
710 class is given an ``__init__()`` method that links to the
711 :paramref:`_orm.registry.constructor` constructor function, if no
712 ``__init__()`` method is already present; this is the usual declarative
713 constructor that will assign keyword arguments as attributes on the
714 instance, assuming those attributes are established at the class level
715 (i.e. are mapped, or are linked to a descriptor). This constructor is
716 **never accessed by a mapped class without being called explicitly via
717 super()**, as mapped classes are themselves given an ``__init__()`` method
718 directly which calls :paramref:`_orm.registry.constructor`, so in the
719 default case works independently of what the base-most ``__init__()``
720 method does.
721
722 .. versionchanged:: 2.0.1 :class:`_orm.DeclarativeBase` has a default
723 constructor that links to :paramref:`_orm.registry.constructor` by
724 default, so that calls to ``super().__init__()`` can access this
725 constructor. Previously, due to an implementation mistake, this default
726 constructor was missing, and calling ``super().__init__()`` would invoke
727 ``object.__init__()``.
728
729 The :class:`_orm.DeclarativeBase` subclass may also declare an explicit
730 ``__init__()`` method which will replace the use of the
731 :paramref:`_orm.registry.constructor` function at this level::
732
733 class Base(DeclarativeBase):
734 def __init__(self, id=None):
735 self.id = id
736
737 Mapped classes still will not invoke this constructor implicitly; it
738 remains only accessible by calling ``super().__init__()``::
739
740 class MyClass(Base):
741 def __init__(self, id=None, name=None):
742 self.name = name
743 super().__init__(id=id)
744
745 Note that this is a different behavior from what functions like the legacy
746 :func:`_orm.declarative_base` would do; the base created by those functions
747 would always install :paramref:`_orm.registry.constructor` for
748 ``__init__()``.
749
750
751 """
752
753 if typing.TYPE_CHECKING:
754
755 def _sa_inspect_type(self) -> Mapper[Self]: ...
756
757 def _sa_inspect_instance(self) -> InstanceState[Self]: ...
758
759 _sa_registry: ClassVar[_RegistryType]
760
761 registry: ClassVar[_RegistryType]
762 """Refers to the :class:`_orm.registry` in use where new
763 :class:`_orm.Mapper` objects will be associated."""
764
765 metadata: ClassVar[MetaData]
766 """Refers to the :class:`_schema.MetaData` collection that will be used
767 for new :class:`_schema.Table` objects.
768
769 .. seealso::
770
771 :ref:`orm_declarative_metadata`
772
773 """
774
775 __name__: ClassVar[str]
776
777 # this ideally should be Mapper[Self], but mypy as of 1.4.1 does not
778 # like it, and breaks the declared_attr_one test. Pyright/pylance is
779 # ok with it.
780 __mapper__: ClassVar[Mapper[Any]]
781 """The :class:`_orm.Mapper` object to which a particular class is
782 mapped.
783
784 May also be acquired using :func:`_sa.inspect`, e.g.
785 ``inspect(klass)``.
786
787 """
788
789 __table__: ClassVar[FromClause]
790 """The :class:`_sql.FromClause` to which a particular subclass is
791 mapped.
792
793 This is usually an instance of :class:`_schema.Table` but may also
794 refer to other kinds of :class:`_sql.FromClause` such as
795 :class:`_sql.Subquery`, depending on how the class is mapped.
796
797 .. seealso::
798
799 :ref:`orm_declarative_metadata`
800
801 """
802
803 # pyright/pylance do not consider a classmethod a ClassVar so use Any
804 # https://github.com/microsoft/pylance-release/issues/3484
805 __tablename__: Any
806 """String name to assign to the generated
807 :class:`_schema.Table` object, if not specified directly via
808 :attr:`_orm.DeclarativeBase.__table__`.
809
810 .. seealso::
811
812 :ref:`orm_declarative_table`
813
814 """
815
816 __mapper_args__: Any
817 """Dictionary of arguments which will be passed to the
818 :class:`_orm.Mapper` constructor.
819
820 .. seealso::
821
822 :ref:`orm_declarative_mapper_options`
823
824 """
825
826 __table_args__: Any
827 """A dictionary or tuple of arguments that will be passed to the
828 :class:`_schema.Table` constructor. See
829 :ref:`orm_declarative_table_configuration`
830 for background on the specific structure of this collection.
831
832 .. seealso::
833
834 :ref:`orm_declarative_table_configuration`
835
836 """
837
838 def __init__(self, **kw: Any): ...
839
840 def __init_subclass__(cls, **kw: Any) -> None:
841 if DeclarativeBase in cls.__bases__:
842 _check_not_declarative(cls, DeclarativeBase)
843 _setup_declarative_base(cls)
844 else:
845 _as_declarative(cls._sa_registry, cls, cls.__dict__)
846 super().__init_subclass__(**kw)
847
848
849def _check_not_declarative(cls: Type[Any], base: Type[Any]) -> None:
850 cls_dict = cls.__dict__
851 if (
852 "__table__" in cls_dict
853 and not (
854 callable(cls_dict["__table__"])
855 or hasattr(cls_dict["__table__"], "__get__")
856 )
857 ) or isinstance(cls_dict.get("__tablename__", None), str):
858 raise exc.InvalidRequestError(
859 f"Cannot use {base.__name__!r} directly as a declarative base "
860 "class. Create a Base by creating a subclass of it."
861 )
862
863
864class DeclarativeBaseNoMeta(
865 # Inspectable is used only by the mypy plugin
866 inspection.Inspectable[InstanceState[Any]]
867):
868 """Same as :class:`_orm.DeclarativeBase`, but does not use a metaclass
869 to intercept new attributes.
870
871 The :class:`_orm.DeclarativeBaseNoMeta` base may be used when use of
872 custom metaclasses is desirable.
873
874 .. versionadded:: 2.0
875
876
877 """
878
879 _sa_registry: ClassVar[_RegistryType]
880
881 registry: ClassVar[_RegistryType]
882 """Refers to the :class:`_orm.registry` in use where new
883 :class:`_orm.Mapper` objects will be associated."""
884
885 metadata: ClassVar[MetaData]
886 """Refers to the :class:`_schema.MetaData` collection that will be used
887 for new :class:`_schema.Table` objects.
888
889 .. seealso::
890
891 :ref:`orm_declarative_metadata`
892
893 """
894
895 # this ideally should be Mapper[Self], but mypy as of 1.4.1 does not
896 # like it, and breaks the declared_attr_one test. Pyright/pylance is
897 # ok with it.
898 __mapper__: ClassVar[Mapper[Any]]
899 """The :class:`_orm.Mapper` object to which a particular class is
900 mapped.
901
902 May also be acquired using :func:`_sa.inspect`, e.g.
903 ``inspect(klass)``.
904
905 """
906
907 __table__: Optional[FromClause]
908 """The :class:`_sql.FromClause` to which a particular subclass is
909 mapped.
910
911 This is usually an instance of :class:`_schema.Table` but may also
912 refer to other kinds of :class:`_sql.FromClause` such as
913 :class:`_sql.Subquery`, depending on how the class is mapped.
914
915 .. seealso::
916
917 :ref:`orm_declarative_metadata`
918
919 """
920
921 if typing.TYPE_CHECKING:
922
923 def _sa_inspect_type(self) -> Mapper[Self]: ...
924
925 def _sa_inspect_instance(self) -> InstanceState[Self]: ...
926
927 __tablename__: Any
928 """String name to assign to the generated
929 :class:`_schema.Table` object, if not specified directly via
930 :attr:`_orm.DeclarativeBase.__table__`.
931
932 .. seealso::
933
934 :ref:`orm_declarative_table`
935
936 """
937
938 __mapper_args__: Any
939 """Dictionary of arguments which will be passed to the
940 :class:`_orm.Mapper` constructor.
941
942 .. seealso::
943
944 :ref:`orm_declarative_mapper_options`
945
946 """
947
948 __table_args__: Any
949 """A dictionary or tuple of arguments that will be passed to the
950 :class:`_schema.Table` constructor. See
951 :ref:`orm_declarative_table_configuration`
952 for background on the specific structure of this collection.
953
954 .. seealso::
955
956 :ref:`orm_declarative_table_configuration`
957
958 """
959
960 def __init__(self, **kw: Any): ...
961
962 def __init_subclass__(cls, **kw: Any) -> None:
963 if DeclarativeBaseNoMeta in cls.__bases__:
964 _check_not_declarative(cls, DeclarativeBaseNoMeta)
965 _setup_declarative_base(cls)
966 else:
967 _as_declarative(cls._sa_registry, cls, cls.__dict__)
968 super().__init_subclass__(**kw)
969
970
971def add_mapped_attribute(
972 target: Type[_O], key: str, attr: MapperProperty[Any]
973) -> None:
974 """Add a new mapped attribute to an ORM mapped class.
975
976 E.g.::
977
978 add_mapped_attribute(User, "addresses", relationship(Address))
979
980 This may be used for ORM mappings that aren't using a declarative
981 metaclass that intercepts attribute set operations.
982
983 .. versionadded:: 2.0
984
985
986 """
987 _add_attribute(target, key, attr)
988
989
990def declarative_base(
991 *,
992 metadata: Optional[MetaData] = None,
993 mapper: Optional[Callable[..., Mapper[Any]]] = None,
994 cls: Type[Any] = object,
995 name: str = "Base",
996 class_registry: Optional[clsregistry._ClsRegistryType] = None,
997 type_annotation_map: Optional[_TypeAnnotationMapType] = None,
998 constructor: Callable[..., None] = _declarative_constructor,
999 metaclass: Type[Any] = DeclarativeMeta,
1000) -> Any:
1001 r"""Construct a base class for declarative class definitions.
1002
1003 The new base class will be given a metaclass that produces
1004 appropriate :class:`~sqlalchemy.schema.Table` objects and makes
1005 the appropriate :class:`_orm.Mapper` calls based on the
1006 information provided declaratively in the class and any subclasses
1007 of the class.
1008
1009 .. versionchanged:: 2.0 Note that the :func:`_orm.declarative_base`
1010 function is superseded by the new :class:`_orm.DeclarativeBase` class,
1011 which generates a new "base" class using subclassing, rather than
1012 return value of a function. This allows an approach that is compatible
1013 with :pep:`484` typing tools.
1014
1015 The :func:`_orm.declarative_base` function is a shorthand version
1016 of using the :meth:`_orm.registry.generate_base`
1017 method. That is, the following::
1018
1019 from sqlalchemy.orm import declarative_base
1020
1021 Base = declarative_base()
1022
1023 Is equivalent to::
1024
1025 from sqlalchemy.orm import registry
1026
1027 mapper_registry = registry()
1028 Base = mapper_registry.generate_base()
1029
1030 See the docstring for :class:`_orm.registry`
1031 and :meth:`_orm.registry.generate_base`
1032 for more details.
1033
1034 .. versionchanged:: 1.4 The :func:`_orm.declarative_base`
1035 function is now a specialization of the more generic
1036 :class:`_orm.registry` class. The function also moves to the
1037 ``sqlalchemy.orm`` package from the ``declarative.ext`` package.
1038
1039
1040 :param metadata:
1041 An optional :class:`~sqlalchemy.schema.MetaData` instance. All
1042 :class:`~sqlalchemy.schema.Table` objects implicitly declared by
1043 subclasses of the base will share this MetaData. A MetaData instance
1044 will be created if none is provided. The
1045 :class:`~sqlalchemy.schema.MetaData` instance will be available via the
1046 ``metadata`` attribute of the generated declarative base class.
1047
1048 :param mapper:
1049 An optional callable, defaults to :class:`_orm.Mapper`. Will
1050 be used to map subclasses to their Tables.
1051
1052 :param cls:
1053 Defaults to :class:`object`. A type to use as the base for the generated
1054 declarative base class. May be a class or tuple of classes.
1055
1056 :param name:
1057 Defaults to ``Base``. The display name for the generated
1058 class. Customizing this is not required, but can improve clarity in
1059 tracebacks and debugging.
1060
1061 :param constructor:
1062 Specify the implementation for the ``__init__`` function on a mapped
1063 class that has no ``__init__`` of its own. Defaults to an
1064 implementation that assigns \**kwargs for declared
1065 fields and relationships to an instance. If ``None`` is supplied,
1066 no __init__ will be provided and construction will fall back to
1067 cls.__init__ by way of the normal Python semantics.
1068
1069 :param class_registry: optional dictionary that will serve as the
1070 registry of class names-> mapped classes when string names
1071 are used to identify classes inside of :func:`_orm.relationship`
1072 and others. Allows two or more declarative base classes
1073 to share the same registry of class names for simplified
1074 inter-base relationships.
1075
1076 :param type_annotation_map: optional dictionary of Python types to
1077 SQLAlchemy :class:`_types.TypeEngine` classes or instances. This
1078 is used exclusively by the :class:`_orm.MappedColumn` construct
1079 to produce column types based on annotations within the
1080 :class:`_orm.Mapped` type.
1081
1082
1083 .. versionadded:: 2.0
1084
1085 .. seealso::
1086
1087 :ref:`orm_declarative_mapped_column_type_map`
1088
1089 :param metaclass:
1090 Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__
1091 compatible callable to use as the meta type of the generated
1092 declarative base class.
1093
1094 .. seealso::
1095
1096 :class:`_orm.registry`
1097
1098 """
1099
1100 return registry(
1101 metadata=metadata,
1102 class_registry=class_registry,
1103 constructor=constructor,
1104 type_annotation_map=type_annotation_map,
1105 ).generate_base(
1106 mapper=mapper,
1107 cls=cls,
1108 name=name,
1109 metaclass=metaclass,
1110 )
1111
1112
1113class registry:
1114 """Generalized registry for mapping classes.
1115
1116 The :class:`_orm.registry` serves as the basis for maintaining a collection
1117 of mappings, and provides configurational hooks used to map classes.
1118
1119 The three general kinds of mappings supported are Declarative Base,
1120 Declarative Decorator, and Imperative Mapping. All of these mapping
1121 styles may be used interchangeably:
1122
1123 * :meth:`_orm.registry.generate_base` returns a new declarative base
1124 class, and is the underlying implementation of the
1125 :func:`_orm.declarative_base` function.
1126
1127 * :meth:`_orm.registry.mapped` provides a class decorator that will
1128 apply declarative mapping to a class without the use of a declarative
1129 base class.
1130
1131 * :meth:`_orm.registry.map_imperatively` will produce a
1132 :class:`_orm.Mapper` for a class without scanning the class for
1133 declarative class attributes. This method suits the use case historically
1134 provided by the ``sqlalchemy.orm.mapper()`` classical mapping function,
1135 which is removed as of SQLAlchemy 2.0.
1136
1137 .. versionadded:: 1.4
1138
1139 .. seealso::
1140
1141 :ref:`orm_mapping_classes_toplevel` - overview of class mapping
1142 styles.
1143
1144 """
1145
1146 _class_registry: clsregistry._ClsRegistryType
1147 _managers: weakref.WeakKeyDictionary[ClassManager[Any], Literal[True]]
1148 _non_primary_mappers: weakref.WeakKeyDictionary[Mapper[Any], Literal[True]]
1149 metadata: MetaData
1150 constructor: CallableReference[Callable[..., None]]
1151 type_annotation_map: _MutableTypeAnnotationMapType
1152 _dependents: Set[_RegistryType]
1153 _dependencies: Set[_RegistryType]
1154 _new_mappers: bool
1155
1156 def __init__(
1157 self,
1158 *,
1159 metadata: Optional[MetaData] = None,
1160 class_registry: Optional[clsregistry._ClsRegistryType] = None,
1161 type_annotation_map: Optional[_TypeAnnotationMapType] = None,
1162 constructor: Callable[..., None] = _declarative_constructor,
1163 ):
1164 r"""Construct a new :class:`_orm.registry`
1165
1166 :param metadata:
1167 An optional :class:`_schema.MetaData` instance. All
1168 :class:`_schema.Table` objects generated using declarative
1169 table mapping will make use of this :class:`_schema.MetaData`
1170 collection. If this argument is left at its default of ``None``,
1171 a blank :class:`_schema.MetaData` collection is created.
1172
1173 :param constructor:
1174 Specify the implementation for the ``__init__`` function on a mapped
1175 class that has no ``__init__`` of its own. Defaults to an
1176 implementation that assigns \**kwargs for declared
1177 fields and relationships to an instance. If ``None`` is supplied,
1178 no __init__ will be provided and construction will fall back to
1179 cls.__init__ by way of the normal Python semantics.
1180
1181 :param class_registry: optional dictionary that will serve as the
1182 registry of class names-> mapped classes when string names
1183 are used to identify classes inside of :func:`_orm.relationship`
1184 and others. Allows two or more declarative base classes
1185 to share the same registry of class names for simplified
1186 inter-base relationships.
1187
1188 :param type_annotation_map: optional dictionary of Python types to
1189 SQLAlchemy :class:`_types.TypeEngine` classes or instances.
1190 The provided dict will update the default type mapping. This
1191 is used exclusively by the :class:`_orm.MappedColumn` construct
1192 to produce column types based on annotations within the
1193 :class:`_orm.Mapped` type.
1194
1195 .. versionadded:: 2.0
1196
1197 .. seealso::
1198
1199 :ref:`orm_declarative_mapped_column_type_map`
1200
1201
1202 """
1203 lcl_metadata = metadata or MetaData()
1204
1205 if class_registry is None:
1206 class_registry = weakref.WeakValueDictionary()
1207
1208 self._class_registry = class_registry
1209 self._managers = weakref.WeakKeyDictionary()
1210 self._non_primary_mappers = weakref.WeakKeyDictionary()
1211 self.metadata = lcl_metadata
1212 self.constructor = constructor
1213 self.type_annotation_map = {}
1214 if type_annotation_map is not None:
1215 self.update_type_annotation_map(type_annotation_map)
1216 self._dependents = set()
1217 self._dependencies = set()
1218
1219 self._new_mappers = False
1220
1221 with mapperlib._CONFIGURE_MUTEX:
1222 mapperlib._mapper_registries[self] = True
1223
1224 def update_type_annotation_map(
1225 self,
1226 type_annotation_map: _TypeAnnotationMapType,
1227 ) -> None:
1228 """update the :paramref:`_orm.registry.type_annotation_map` with new
1229 values."""
1230
1231 self.type_annotation_map.update(
1232 {
1233 de_optionalize_union_types(typ): sqltype
1234 for typ, sqltype in type_annotation_map.items()
1235 }
1236 )
1237
1238 def _resolve_type(
1239 self, python_type: _MatchedOnType, _do_fallbacks: bool = True
1240 ) -> Optional[sqltypes.TypeEngine[Any]]:
1241 python_type_type: Type[Any]
1242 search: Iterable[Tuple[_MatchedOnType, Type[Any]]]
1243
1244 if is_generic(python_type):
1245 if is_literal(python_type):
1246 python_type_type = python_type # type: ignore[assignment]
1247
1248 search = (
1249 (python_type, python_type_type),
1250 *((lt, python_type_type) for lt in LITERAL_TYPES),
1251 )
1252 else:
1253 python_type_type = python_type.__origin__
1254 search = ((python_type, python_type_type),)
1255 elif isinstance(python_type, type):
1256 python_type_type = python_type
1257 search = ((pt, pt) for pt in python_type_type.__mro__)
1258 else:
1259 python_type_type = python_type # type: ignore[assignment]
1260 search = ((python_type, python_type_type),)
1261
1262 for pt, flattened in search:
1263 # we search through full __mro__ for types. however...
1264 sql_type = self.type_annotation_map.get(pt)
1265 if sql_type is None:
1266 sql_type = sqltypes._type_map_get(pt) # type: ignore # noqa: E501
1267
1268 if sql_type is not None:
1269 sql_type_inst = sqltypes.to_instance(sql_type)
1270
1271 # ... this additional step will reject most
1272 # type -> supertype matches, such as if we had
1273 # a MyInt(int) subclass. note also we pass NewType()
1274 # here directly; these always have to be in the
1275 # type_annotation_map to be useful
1276 resolved_sql_type = sql_type_inst._resolve_for_python_type(
1277 python_type_type,
1278 pt,
1279 flattened,
1280 )
1281 if resolved_sql_type is not None:
1282 return resolved_sql_type
1283
1284 # 2.0 fallbacks
1285 if _do_fallbacks:
1286 python_type_to_check: Any = None
1287 kind = None
1288 if is_pep695(python_type):
1289 # NOTE: assume there aren't type alias types of new types.
1290 python_type_to_check = python_type
1291 while is_pep695(python_type_to_check):
1292 python_type_to_check = python_type_to_check.__value__
1293 python_type_to_check = de_optionalize_union_types(
1294 python_type_to_check
1295 )
1296 kind = "TypeAliasType"
1297 if is_newtype(python_type):
1298 python_type_to_check = flatten_newtype(python_type)
1299 kind = "NewType"
1300
1301 if python_type_to_check is not None:
1302 res_after_fallback = self._resolve_type(
1303 python_type_to_check, False
1304 )
1305 if res_after_fallback is not None:
1306 assert kind is not None
1307 warn_deprecated(
1308 f"Matching the provided {kind} '{python_type}' on "
1309 "its resolved value without matching it in the "
1310 "type_annotation_map is deprecated; add this type to "
1311 "the type_annotation_map to allow it to match "
1312 "explicitly.",
1313 "2.0",
1314 )
1315 return res_after_fallback
1316
1317 return None
1318
1319 @property
1320 def mappers(self) -> FrozenSet[Mapper[Any]]:
1321 """read only collection of all :class:`_orm.Mapper` objects."""
1322
1323 return frozenset(manager.mapper for manager in self._managers).union(
1324 self._non_primary_mappers
1325 )
1326
1327 def _set_depends_on(self, registry: RegistryType) -> None:
1328 if registry is self:
1329 return
1330 registry._dependents.add(self)
1331 self._dependencies.add(registry)
1332
1333 def _flag_new_mapper(self, mapper: Mapper[Any]) -> None:
1334 mapper._ready_for_configure = True
1335 if self._new_mappers:
1336 return
1337
1338 for reg in self._recurse_with_dependents({self}):
1339 reg._new_mappers = True
1340
1341 @classmethod
1342 def _recurse_with_dependents(
1343 cls, registries: Set[RegistryType]
1344 ) -> Iterator[RegistryType]:
1345 todo = registries
1346 done = set()
1347 while todo:
1348 reg = todo.pop()
1349 done.add(reg)
1350
1351 # if yielding would remove dependents, make sure we have
1352 # them before
1353 todo.update(reg._dependents.difference(done))
1354 yield reg
1355
1356 # if yielding would add dependents, make sure we have them
1357 # after
1358 todo.update(reg._dependents.difference(done))
1359
1360 @classmethod
1361 def _recurse_with_dependencies(
1362 cls, registries: Set[RegistryType]
1363 ) -> Iterator[RegistryType]:
1364 todo = registries
1365 done = set()
1366 while todo:
1367 reg = todo.pop()
1368 done.add(reg)
1369
1370 # if yielding would remove dependencies, make sure we have
1371 # them before
1372 todo.update(reg._dependencies.difference(done))
1373
1374 yield reg
1375
1376 # if yielding would remove dependencies, make sure we have
1377 # them before
1378 todo.update(reg._dependencies.difference(done))
1379
1380 def _mappers_to_configure(self) -> Iterator[Mapper[Any]]:
1381 return itertools.chain(
1382 (
1383 manager.mapper
1384 for manager in list(self._managers)
1385 if manager.is_mapped
1386 and not manager.mapper.configured
1387 and manager.mapper._ready_for_configure
1388 ),
1389 (
1390 npm
1391 for npm in list(self._non_primary_mappers)
1392 if not npm.configured and npm._ready_for_configure
1393 ),
1394 )
1395
1396 def _add_non_primary_mapper(self, np_mapper: Mapper[Any]) -> None:
1397 self._non_primary_mappers[np_mapper] = True
1398
1399 def _dispose_cls(self, cls: Type[_O]) -> None:
1400 clsregistry.remove_class(cls.__name__, cls, self._class_registry)
1401
1402 def _add_manager(self, manager: ClassManager[Any]) -> None:
1403 self._managers[manager] = True
1404 if manager.is_mapped:
1405 raise exc.ArgumentError(
1406 "Class '%s' already has a primary mapper defined. "
1407 % manager.class_
1408 )
1409 assert manager.registry is None
1410 manager.registry = self
1411
1412 def configure(self, cascade: bool = False) -> None:
1413 """Configure all as-yet unconfigured mappers in this
1414 :class:`_orm.registry`.
1415
1416 The configure step is used to reconcile and initialize the
1417 :func:`_orm.relationship` linkages between mapped classes, as well as
1418 to invoke configuration events such as the
1419 :meth:`_orm.MapperEvents.before_configured` and
1420 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM
1421 extensions or user-defined extension hooks.
1422
1423 If one or more mappers in this registry contain
1424 :func:`_orm.relationship` constructs that refer to mapped classes in
1425 other registries, this registry is said to be *dependent* on those
1426 registries. In order to configure those dependent registries
1427 automatically, the :paramref:`_orm.registry.configure.cascade` flag
1428 should be set to ``True``. Otherwise, if they are not configured, an
1429 exception will be raised. The rationale behind this behavior is to
1430 allow an application to programmatically invoke configuration of
1431 registries while controlling whether or not the process implicitly
1432 reaches other registries.
1433
1434 As an alternative to invoking :meth:`_orm.registry.configure`, the ORM
1435 function :func:`_orm.configure_mappers` function may be used to ensure
1436 configuration is complete for all :class:`_orm.registry` objects in
1437 memory. This is generally simpler to use and also predates the usage of
1438 :class:`_orm.registry` objects overall. However, this function will
1439 impact all mappings throughout the running Python process and may be
1440 more memory/time consuming for an application that has many registries
1441 in use for different purposes that may not be needed immediately.
1442
1443 .. seealso::
1444
1445 :func:`_orm.configure_mappers`
1446
1447
1448 .. versionadded:: 1.4.0b2
1449
1450 """
1451 mapperlib._configure_registries({self}, cascade=cascade)
1452
1453 def dispose(self, cascade: bool = False) -> None:
1454 """Dispose of all mappers in this :class:`_orm.registry`.
1455
1456 After invocation, all the classes that were mapped within this registry
1457 will no longer have class instrumentation associated with them. This
1458 method is the per-:class:`_orm.registry` analogue to the
1459 application-wide :func:`_orm.clear_mappers` function.
1460
1461 If this registry contains mappers that are dependencies of other
1462 registries, typically via :func:`_orm.relationship` links, then those
1463 registries must be disposed as well. When such registries exist in
1464 relation to this one, their :meth:`_orm.registry.dispose` method will
1465 also be called, if the :paramref:`_orm.registry.dispose.cascade` flag
1466 is set to ``True``; otherwise, an error is raised if those registries
1467 were not already disposed.
1468
1469 .. versionadded:: 1.4.0b2
1470
1471 .. seealso::
1472
1473 :func:`_orm.clear_mappers`
1474
1475 """
1476
1477 mapperlib._dispose_registries({self}, cascade=cascade)
1478
1479 def _dispose_manager_and_mapper(self, manager: ClassManager[Any]) -> None:
1480 if "mapper" in manager.__dict__:
1481 mapper = manager.mapper
1482
1483 mapper._set_dispose_flags()
1484
1485 class_ = manager.class_
1486 self._dispose_cls(class_)
1487 instrumentation._instrumentation_factory.unregister(class_)
1488
1489 def generate_base(
1490 self,
1491 mapper: Optional[Callable[..., Mapper[Any]]] = None,
1492 cls: Type[Any] = object,
1493 name: str = "Base",
1494 metaclass: Type[Any] = DeclarativeMeta,
1495 ) -> Any:
1496 """Generate a declarative base class.
1497
1498 Classes that inherit from the returned class object will be
1499 automatically mapped using declarative mapping.
1500
1501 E.g.::
1502
1503 from sqlalchemy.orm import registry
1504
1505 mapper_registry = registry()
1506
1507 Base = mapper_registry.generate_base()
1508
1509
1510 class MyClass(Base):
1511 __tablename__ = "my_table"
1512 id = Column(Integer, primary_key=True)
1513
1514 The above dynamically generated class is equivalent to the
1515 non-dynamic example below::
1516
1517 from sqlalchemy.orm import registry
1518 from sqlalchemy.orm.decl_api import DeclarativeMeta
1519
1520 mapper_registry = registry()
1521
1522
1523 class Base(metaclass=DeclarativeMeta):
1524 __abstract__ = True
1525 registry = mapper_registry
1526 metadata = mapper_registry.metadata
1527
1528 __init__ = mapper_registry.constructor
1529
1530 .. versionchanged:: 2.0 Note that the
1531 :meth:`_orm.registry.generate_base` method is superseded by the new
1532 :class:`_orm.DeclarativeBase` class, which generates a new "base"
1533 class using subclassing, rather than return value of a function.
1534 This allows an approach that is compatible with :pep:`484` typing
1535 tools.
1536
1537 The :meth:`_orm.registry.generate_base` method provides the
1538 implementation for the :func:`_orm.declarative_base` function, which
1539 creates the :class:`_orm.registry` and base class all at once.
1540
1541 See the section :ref:`orm_declarative_mapping` for background and
1542 examples.
1543
1544 :param mapper:
1545 An optional callable, defaults to :class:`_orm.Mapper`.
1546 This function is used to generate new :class:`_orm.Mapper` objects.
1547
1548 :param cls:
1549 Defaults to :class:`object`. A type to use as the base for the
1550 generated declarative base class. May be a class or tuple of classes.
1551
1552 :param name:
1553 Defaults to ``Base``. The display name for the generated
1554 class. Customizing this is not required, but can improve clarity in
1555 tracebacks and debugging.
1556
1557 :param metaclass:
1558 Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__
1559 compatible callable to use as the meta type of the generated
1560 declarative base class.
1561
1562 .. seealso::
1563
1564 :ref:`orm_declarative_mapping`
1565
1566 :func:`_orm.declarative_base`
1567
1568 """
1569 metadata = self.metadata
1570
1571 bases = not isinstance(cls, tuple) and (cls,) or cls
1572
1573 class_dict: Dict[str, Any] = dict(registry=self, metadata=metadata)
1574 if isinstance(cls, type):
1575 class_dict["__doc__"] = cls.__doc__
1576
1577 if self.constructor is not None:
1578 class_dict["__init__"] = self.constructor
1579
1580 class_dict["__abstract__"] = True
1581 if mapper:
1582 class_dict["__mapper_cls__"] = mapper
1583
1584 if hasattr(cls, "__class_getitem__"):
1585
1586 def __class_getitem__(cls: Type[_T], key: Any) -> Type[_T]:
1587 # allow generic classes in py3.9+
1588 return cls
1589
1590 class_dict["__class_getitem__"] = __class_getitem__
1591
1592 return metaclass(name, bases, class_dict)
1593
1594 @compat_typing.dataclass_transform(
1595 field_specifiers=(
1596 MappedColumn,
1597 RelationshipProperty,
1598 Composite,
1599 Synonym,
1600 mapped_column,
1601 relationship,
1602 composite,
1603 synonym,
1604 deferred,
1605 ),
1606 )
1607 @overload
1608 def mapped_as_dataclass(self, __cls: Type[_O]) -> Type[_O]: ...
1609
1610 @overload
1611 def mapped_as_dataclass(
1612 self,
1613 __cls: Literal[None] = ...,
1614 *,
1615 init: Union[_NoArg, bool] = ...,
1616 repr: Union[_NoArg, bool] = ..., # noqa: A002
1617 eq: Union[_NoArg, bool] = ...,
1618 order: Union[_NoArg, bool] = ...,
1619 unsafe_hash: Union[_NoArg, bool] = ...,
1620 match_args: Union[_NoArg, bool] = ...,
1621 kw_only: Union[_NoArg, bool] = ...,
1622 dataclass_callable: Union[_NoArg, Callable[..., Type[Any]]] = ...,
1623 ) -> Callable[[Type[_O]], Type[_O]]: ...
1624
1625 def mapped_as_dataclass(
1626 self,
1627 __cls: Optional[Type[_O]] = None,
1628 *,
1629 init: Union[_NoArg, bool] = _NoArg.NO_ARG,
1630 repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
1631 eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
1632 order: Union[_NoArg, bool] = _NoArg.NO_ARG,
1633 unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
1634 match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
1635 kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
1636 dataclass_callable: Union[
1637 _NoArg, Callable[..., Type[Any]]
1638 ] = _NoArg.NO_ARG,
1639 ) -> Union[Type[_O], Callable[[Type[_O]], Type[_O]]]:
1640 """Class decorator that will apply the Declarative mapping process
1641 to a given class, and additionally convert the class to be a
1642 Python dataclass.
1643
1644 .. seealso::
1645
1646 :ref:`orm_declarative_native_dataclasses` - complete background
1647 on SQLAlchemy native dataclass mapping
1648
1649
1650 .. versionadded:: 2.0
1651
1652
1653 """
1654
1655 def decorate(cls: Type[_O]) -> Type[_O]:
1656 setattr(
1657 cls,
1658 "_sa_apply_dc_transforms",
1659 {
1660 "init": init,
1661 "repr": repr,
1662 "eq": eq,
1663 "order": order,
1664 "unsafe_hash": unsafe_hash,
1665 "match_args": match_args,
1666 "kw_only": kw_only,
1667 "dataclass_callable": dataclass_callable,
1668 },
1669 )
1670 _as_declarative(self, cls, cls.__dict__)
1671 return cls
1672
1673 if __cls:
1674 return decorate(__cls)
1675 else:
1676 return decorate
1677
1678 def mapped(self, cls: Type[_O]) -> Type[_O]:
1679 """Class decorator that will apply the Declarative mapping process
1680 to a given class.
1681
1682 E.g.::
1683
1684 from sqlalchemy.orm import registry
1685
1686 mapper_registry = registry()
1687
1688
1689 @mapper_registry.mapped
1690 class Foo:
1691 __tablename__ = "some_table"
1692
1693 id = Column(Integer, primary_key=True)
1694 name = Column(String)
1695
1696 See the section :ref:`orm_declarative_mapping` for complete
1697 details and examples.
1698
1699 :param cls: class to be mapped.
1700
1701 :return: the class that was passed.
1702
1703 .. seealso::
1704
1705 :ref:`orm_declarative_mapping`
1706
1707 :meth:`_orm.registry.generate_base` - generates a base class
1708 that will apply Declarative mapping to subclasses automatically
1709 using a Python metaclass.
1710
1711 .. seealso::
1712
1713 :meth:`_orm.registry.mapped_as_dataclass`
1714
1715 """
1716 _as_declarative(self, cls, cls.__dict__)
1717 return cls
1718
1719 def as_declarative_base(self, **kw: Any) -> Callable[[Type[_T]], Type[_T]]:
1720 """
1721 Class decorator which will invoke
1722 :meth:`_orm.registry.generate_base`
1723 for a given base class.
1724
1725 E.g.::
1726
1727 from sqlalchemy.orm import registry
1728
1729 mapper_registry = registry()
1730
1731
1732 @mapper_registry.as_declarative_base()
1733 class Base:
1734 @declared_attr
1735 def __tablename__(cls):
1736 return cls.__name__.lower()
1737
1738 id = Column(Integer, primary_key=True)
1739
1740
1741 class MyMappedClass(Base): ...
1742
1743 All keyword arguments passed to
1744 :meth:`_orm.registry.as_declarative_base` are passed
1745 along to :meth:`_orm.registry.generate_base`.
1746
1747 """
1748
1749 def decorate(cls: Type[_T]) -> Type[_T]:
1750 kw["cls"] = cls
1751 kw["name"] = cls.__name__
1752 return self.generate_base(**kw) # type: ignore
1753
1754 return decorate
1755
1756 def map_declaratively(self, cls: Type[_O]) -> Mapper[_O]:
1757 """Map a class declaratively.
1758
1759 In this form of mapping, the class is scanned for mapping information,
1760 including for columns to be associated with a table, and/or an
1761 actual table object.
1762
1763 Returns the :class:`_orm.Mapper` object.
1764
1765 E.g.::
1766
1767 from sqlalchemy.orm import registry
1768
1769 mapper_registry = registry()
1770
1771
1772 class Foo:
1773 __tablename__ = "some_table"
1774
1775 id = Column(Integer, primary_key=True)
1776 name = Column(String)
1777
1778
1779 mapper = mapper_registry.map_declaratively(Foo)
1780
1781 This function is more conveniently invoked indirectly via either the
1782 :meth:`_orm.registry.mapped` class decorator or by subclassing a
1783 declarative metaclass generated from
1784 :meth:`_orm.registry.generate_base`.
1785
1786 See the section :ref:`orm_declarative_mapping` for complete
1787 details and examples.
1788
1789 :param cls: class to be mapped.
1790
1791 :return: a :class:`_orm.Mapper` object.
1792
1793 .. seealso::
1794
1795 :ref:`orm_declarative_mapping`
1796
1797 :meth:`_orm.registry.mapped` - more common decorator interface
1798 to this function.
1799
1800 :meth:`_orm.registry.map_imperatively`
1801
1802 """
1803 _as_declarative(self, cls, cls.__dict__)
1804 return cls.__mapper__ # type: ignore
1805
1806 def map_imperatively(
1807 self,
1808 class_: Type[_O],
1809 local_table: Optional[FromClause] = None,
1810 **kw: Any,
1811 ) -> Mapper[_O]:
1812 r"""Map a class imperatively.
1813
1814 In this form of mapping, the class is not scanned for any mapping
1815 information. Instead, all mapping constructs are passed as
1816 arguments.
1817
1818 This method is intended to be fully equivalent to the now-removed
1819 SQLAlchemy ``mapper()`` function, except that it's in terms of
1820 a particular registry.
1821
1822 E.g.::
1823
1824 from sqlalchemy.orm import registry
1825
1826 mapper_registry = registry()
1827
1828 my_table = Table(
1829 "my_table",
1830 mapper_registry.metadata,
1831 Column("id", Integer, primary_key=True),
1832 )
1833
1834
1835 class MyClass:
1836 pass
1837
1838
1839 mapper_registry.map_imperatively(MyClass, my_table)
1840
1841 See the section :ref:`orm_imperative_mapping` for complete background
1842 and usage examples.
1843
1844 :param class\_: The class to be mapped. Corresponds to the
1845 :paramref:`_orm.Mapper.class_` parameter.
1846
1847 :param local_table: the :class:`_schema.Table` or other
1848 :class:`_sql.FromClause` object that is the subject of the mapping.
1849 Corresponds to the
1850 :paramref:`_orm.Mapper.local_table` parameter.
1851
1852 :param \**kw: all other keyword arguments are passed to the
1853 :class:`_orm.Mapper` constructor directly.
1854
1855 .. seealso::
1856
1857 :ref:`orm_imperative_mapping`
1858
1859 :ref:`orm_declarative_mapping`
1860
1861 """
1862 return _mapper(self, class_, local_table, kw)
1863
1864
1865RegistryType = registry
1866
1867if not TYPE_CHECKING:
1868 # allow for runtime type resolution of ``ClassVar[_RegistryType]``
1869 _RegistryType = registry # noqa
1870
1871
1872def as_declarative(**kw: Any) -> Callable[[Type[_T]], Type[_T]]:
1873 """
1874 Class decorator which will adapt a given class into a
1875 :func:`_orm.declarative_base`.
1876
1877 This function makes use of the :meth:`_orm.registry.as_declarative_base`
1878 method, by first creating a :class:`_orm.registry` automatically
1879 and then invoking the decorator.
1880
1881 E.g.::
1882
1883 from sqlalchemy.orm import as_declarative
1884
1885
1886 @as_declarative()
1887 class Base:
1888 @declared_attr
1889 def __tablename__(cls):
1890 return cls.__name__.lower()
1891
1892 id = Column(Integer, primary_key=True)
1893
1894
1895 class MyMappedClass(Base): ...
1896
1897 .. seealso::
1898
1899 :meth:`_orm.registry.as_declarative_base`
1900
1901 """
1902 metadata, class_registry = (
1903 kw.pop("metadata", None),
1904 kw.pop("class_registry", None),
1905 )
1906
1907 return registry(
1908 metadata=metadata, class_registry=class_registry
1909 ).as_declarative_base(**kw)
1910
1911
1912@inspection._inspects(
1913 DeclarativeMeta, DeclarativeBase, DeclarativeAttributeIntercept
1914)
1915def _inspect_decl_meta(cls: Type[Any]) -> Optional[Mapper[Any]]:
1916 mp: Optional[Mapper[Any]] = _inspect_mapped_class(cls)
1917 if mp is None:
1918 if _DeferredMapperConfig.has_cls(cls):
1919 _DeferredMapperConfig.raise_unmapped_for_cls(cls)
1920 return mp