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