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