| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920 |
- # orm/decl_api.py
- # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
- # <see AUTHORS file>
- #
- # This module is part of SQLAlchemy and is released under
- # the MIT License: https://www.opensource.org/licenses/mit-license.php
- """Public API functions and helpers for declarative."""
- from __future__ import annotations
- import itertools
- import re
- import typing
- from typing import Any
- from typing import Callable
- from typing import ClassVar
- from typing import Dict
- from typing import FrozenSet
- from typing import Generic
- from typing import Iterable
- from typing import Iterator
- from typing import Mapping
- from typing import Optional
- from typing import overload
- from typing import Set
- from typing import Tuple
- from typing import Type
- from typing import TYPE_CHECKING
- from typing import TypeVar
- from typing import Union
- import weakref
- from . import attributes
- from . import clsregistry
- from . import instrumentation
- from . import interfaces
- from . import mapperlib
- from ._orm_constructors import composite
- from ._orm_constructors import deferred
- from ._orm_constructors import mapped_column
- from ._orm_constructors import relationship
- from ._orm_constructors import synonym
- from .attributes import InstrumentedAttribute
- from .base import _inspect_mapped_class
- from .base import _is_mapped_class
- from .base import Mapped
- from .base import ORMDescriptor
- from .decl_base import _add_attribute
- from .decl_base import _as_declarative
- from .decl_base import _ClassScanMapperConfig
- from .decl_base import _declarative_constructor
- from .decl_base import _DeferredMapperConfig
- from .decl_base import _del_attribute
- from .decl_base import _mapper
- from .descriptor_props import Composite
- from .descriptor_props import Synonym
- from .descriptor_props import Synonym as _orm_synonym
- from .mapper import Mapper
- from .properties import MappedColumn
- from .relationships import RelationshipProperty
- from .state import InstanceState
- from .. import exc
- from .. import inspection
- from .. import util
- from ..sql import sqltypes
- from ..sql.base import _NoArg
- from ..sql.elements import SQLCoreOperations
- from ..sql.schema import MetaData
- from ..sql.selectable import FromClause
- from ..util import hybridmethod
- from ..util import hybridproperty
- from ..util import typing as compat_typing
- from ..util import warn_deprecated
- from ..util.typing import CallableReference
- from ..util.typing import de_optionalize_union_types
- from ..util.typing import flatten_newtype
- from ..util.typing import is_generic
- from ..util.typing import is_literal
- from ..util.typing import is_newtype
- from ..util.typing import is_pep695
- from ..util.typing import Literal
- from ..util.typing import LITERAL_TYPES
- from ..util.typing import Self
- if TYPE_CHECKING:
- from ._typing import _O
- from ._typing import _RegistryType
- from .decl_base import _DataclassArguments
- from .instrumentation import ClassManager
- from .interfaces import MapperProperty
- from .state import InstanceState # noqa
- from ..sql._typing import _TypeEngineArgument
- from ..sql.type_api import _MatchedOnType
- _T = TypeVar("_T", bound=Any)
- _TT = TypeVar("_TT", bound=Any)
- # it's not clear how to have Annotated, Union objects etc. as keys here
- # from a typing perspective so just leave it open ended for now
- _TypeAnnotationMapType = Mapping[Any, "_TypeEngineArgument[Any]"]
- _MutableTypeAnnotationMapType = Dict[Any, "_TypeEngineArgument[Any]"]
- _DeclaredAttrDecorated = Callable[
- ..., Union[Mapped[_T], ORMDescriptor[_T], SQLCoreOperations[_T]]
- ]
- def has_inherited_table(cls: Type[_O]) -> bool:
- """Given a class, return True if any of the classes it inherits from has a
- mapped table, otherwise return False.
- This is used in declarative mixins to build attributes that behave
- differently for the base class vs. a subclass in an inheritance
- hierarchy.
- .. seealso::
- :ref:`decl_mixin_inheritance`
- """
- for class_ in cls.__mro__[1:]:
- if getattr(class_, "__table__", None) is not None:
- return True
- return False
- class _DynamicAttributesType(type):
- def __setattr__(cls, key: str, value: Any) -> None:
- if "__mapper__" in cls.__dict__:
- _add_attribute(cls, key, value)
- else:
- type.__setattr__(cls, key, value)
- def __delattr__(cls, key: str) -> None:
- if "__mapper__" in cls.__dict__:
- _del_attribute(cls, key)
- else:
- type.__delattr__(cls, key)
- class DeclarativeAttributeIntercept(
- _DynamicAttributesType,
- # Inspectable is used only by the mypy plugin
- inspection.Inspectable[Mapper[Any]],
- ):
- """Metaclass that may be used in conjunction with the
- :class:`_orm.DeclarativeBase` class to support addition of class
- attributes dynamically.
- """
- @compat_typing.dataclass_transform(
- field_specifiers=(
- MappedColumn,
- RelationshipProperty,
- Composite,
- Synonym,
- mapped_column,
- relationship,
- composite,
- synonym,
- deferred,
- ),
- )
- class DCTransformDeclarative(DeclarativeAttributeIntercept):
- """metaclass that includes @dataclass_transforms"""
- class DeclarativeMeta(DeclarativeAttributeIntercept):
- metadata: MetaData
- registry: RegistryType
- def __init__(
- cls, classname: Any, bases: Any, dict_: Any, **kw: Any
- ) -> None:
- # use cls.__dict__, which can be modified by an
- # __init_subclass__() method (#7900)
- dict_ = cls.__dict__
- # early-consume registry from the initial declarative base,
- # assign privately to not conflict with subclass attributes named
- # "registry"
- reg = getattr(cls, "_sa_registry", None)
- if reg is None:
- reg = dict_.get("registry", None)
- if not isinstance(reg, registry):
- raise exc.InvalidRequestError(
- "Declarative base class has no 'registry' attribute, "
- "or registry is not a sqlalchemy.orm.registry() object"
- )
- else:
- cls._sa_registry = reg
- if not cls.__dict__.get("__abstract__", False):
- _as_declarative(reg, cls, dict_)
- type.__init__(cls, classname, bases, dict_)
- def synonym_for(
- name: str, map_column: bool = False
- ) -> Callable[[Callable[..., Any]], Synonym[Any]]:
- """Decorator that produces an :func:`_orm.synonym`
- attribute in conjunction with a Python descriptor.
- The function being decorated is passed to :func:`_orm.synonym` as the
- :paramref:`.orm.synonym.descriptor` parameter::
- class MyClass(Base):
- __tablename__ = "my_table"
- id = Column(Integer, primary_key=True)
- _job_status = Column("job_status", String(50))
- @synonym_for("job_status")
- @property
- def job_status(self):
- return "Status: %s" % self._job_status
- The :ref:`hybrid properties <mapper_hybrids>` feature of SQLAlchemy
- is typically preferred instead of synonyms, which is a more legacy
- feature.
- .. seealso::
- :ref:`synonyms` - Overview of synonyms
- :func:`_orm.synonym` - the mapper-level function
- :ref:`mapper_hybrids` - The Hybrid Attribute extension provides an
- updated approach to augmenting attribute behavior more flexibly than
- can be achieved with synonyms.
- """
- def decorate(fn: Callable[..., Any]) -> Synonym[Any]:
- return _orm_synonym(name, map_column=map_column, descriptor=fn)
- return decorate
- class _declared_attr_common:
- def __init__(
- self,
- fn: Callable[..., Any],
- cascading: bool = False,
- quiet: bool = False,
- ):
- # suppport
- # @declared_attr
- # @classmethod
- # def foo(cls) -> Mapped[thing]:
- # ...
- # which seems to help typing tools interpret the fn as a classmethod
- # for situations where needed
- if isinstance(fn, classmethod):
- fn = fn.__func__
- self.fget = fn
- self._cascading = cascading
- self._quiet = quiet
- self.__doc__ = fn.__doc__
- def _collect_return_annotation(self) -> Optional[Type[Any]]:
- return util.get_annotations(self.fget).get("return")
- def __get__(self, instance: Optional[object], owner: Any) -> Any:
- # the declared_attr needs to make use of a cache that exists
- # for the span of the declarative scan_attributes() phase.
- # to achieve this we look at the class manager that's configured.
- # note this method should not be called outside of the declarative
- # setup phase
- cls = owner
- manager = attributes.opt_manager_of_class(cls)
- if manager is None:
- if not re.match(r"^__.+__$", self.fget.__name__):
- # if there is no manager at all, then this class hasn't been
- # run through declarative or mapper() at all, emit a warning.
- util.warn(
- "Unmanaged access of declarative attribute %s from "
- "non-mapped class %s" % (self.fget.__name__, cls.__name__)
- )
- return self.fget(cls)
- elif manager.is_mapped:
- # the class is mapped, which means we're outside of the declarative
- # scan setup, just run the function.
- return self.fget(cls)
- # here, we are inside of the declarative scan. use the registry
- # that is tracking the values of these attributes.
- declarative_scan = manager.declarative_scan()
- # assert that we are in fact in the declarative scan
- assert declarative_scan is not None
- reg = declarative_scan.declared_attr_reg
- if self in reg:
- return reg[self]
- else:
- reg[self] = obj = self.fget(cls)
- return obj
- class _declared_directive(_declared_attr_common, Generic[_T]):
- # see mapping_api.rst for docstring
- if typing.TYPE_CHECKING:
- def __init__(
- self,
- fn: Callable[..., _T],
- cascading: bool = False,
- ): ...
- def __get__(self, instance: Optional[object], owner: Any) -> _T: ...
- def __set__(self, instance: Any, value: Any) -> None: ...
- def __delete__(self, instance: Any) -> None: ...
- def __call__(self, fn: Callable[..., _TT]) -> _declared_directive[_TT]:
- # extensive fooling of mypy underway...
- ...
- class declared_attr(interfaces._MappedAttribute[_T], _declared_attr_common):
- """Mark a class-level method as representing the definition of
- a mapped property or Declarative directive.
- :class:`_orm.declared_attr` is typically applied as a decorator to a class
- level method, turning the attribute into a scalar-like property that can be
- invoked from the uninstantiated class. The Declarative mapping process
- looks for these :class:`_orm.declared_attr` callables as it scans classes,
- and assumes any attribute marked with :class:`_orm.declared_attr` will be a
- callable that will produce an object specific to the Declarative mapping or
- table configuration.
- :class:`_orm.declared_attr` is usually applicable to
- :ref:`mixins <orm_mixins_toplevel>`, to define relationships that are to be
- applied to different implementors of the class. It may also be used to
- define dynamically generated column expressions and other Declarative
- attributes.
- Example::
- class ProvidesUserMixin:
- "A mixin that adds a 'user' relationship to classes."
- user_id: Mapped[int] = mapped_column(ForeignKey("user_table.id"))
- @declared_attr
- def user(cls) -> Mapped["User"]:
- return relationship("User")
- When used with Declarative directives such as ``__tablename__``, the
- :meth:`_orm.declared_attr.directive` modifier may be used which indicates
- to :pep:`484` typing tools that the given method is not dealing with
- :class:`_orm.Mapped` attributes::
- class CreateTableName:
- @declared_attr.directive
- def __tablename__(cls) -> str:
- return cls.__name__.lower()
- :class:`_orm.declared_attr` can also be applied directly to mapped
- classes, to allow for attributes that dynamically configure themselves
- on subclasses when using mapped inheritance schemes. Below
- illustrates :class:`_orm.declared_attr` to create a dynamic scheme
- for generating the :paramref:`_orm.Mapper.polymorphic_identity` parameter
- for subclasses::
- class Employee(Base):
- __tablename__ = "employee"
- id: Mapped[int] = mapped_column(primary_key=True)
- type: Mapped[str] = mapped_column(String(50))
- @declared_attr.directive
- def __mapper_args__(cls) -> Dict[str, Any]:
- if cls.__name__ == "Employee":
- return {
- "polymorphic_on": cls.type,
- "polymorphic_identity": "Employee",
- }
- else:
- return {"polymorphic_identity": cls.__name__}
- class Engineer(Employee):
- pass
- :class:`_orm.declared_attr` supports decorating functions that are
- explicitly decorated with ``@classmethod``. This is never necessary from a
- runtime perspective, however may be needed in order to support :pep:`484`
- typing tools that don't otherwise recognize the decorated function as
- having class-level behaviors for the ``cls`` parameter::
- class SomethingMixin:
- x: Mapped[int]
- y: Mapped[int]
- @declared_attr
- @classmethod
- def x_plus_y(cls) -> Mapped[int]:
- return column_property(cls.x + cls.y)
- .. versionadded:: 2.0 - :class:`_orm.declared_attr` can accommodate a
- function decorated with ``@classmethod`` to help with :pep:`484`
- integration where needed.
- .. seealso::
- :ref:`orm_mixins_toplevel` - Declarative Mixin documentation with
- background on use patterns for :class:`_orm.declared_attr`.
- """ # noqa: E501
- if typing.TYPE_CHECKING:
- def __init__(
- self,
- fn: _DeclaredAttrDecorated[_T],
- cascading: bool = False,
- ): ...
- def __set__(self, instance: Any, value: Any) -> None: ...
- def __delete__(self, instance: Any) -> None: ...
- # this is the Mapped[] API where at class descriptor get time we want
- # the type checker to see InstrumentedAttribute[_T]. However the
- # callable function prior to mapping in fact calls the given
- # declarative function that does not return InstrumentedAttribute
- @overload
- def __get__(
- self, instance: None, owner: Any
- ) -> InstrumentedAttribute[_T]: ...
- @overload
- def __get__(self, instance: object, owner: Any) -> _T: ...
- def __get__(
- self, instance: Optional[object], owner: Any
- ) -> Union[InstrumentedAttribute[_T], _T]: ...
- @hybridmethod
- def _stateful(cls, **kw: Any) -> _stateful_declared_attr[_T]:
- return _stateful_declared_attr(**kw)
- @hybridproperty
- def directive(cls) -> _declared_directive[Any]:
- # see mapping_api.rst for docstring
- return _declared_directive # type: ignore
- @hybridproperty
- def cascading(cls) -> _stateful_declared_attr[_T]:
- # see mapping_api.rst for docstring
- return cls._stateful(cascading=True)
- class _stateful_declared_attr(declared_attr[_T]):
- kw: Dict[str, Any]
- def __init__(self, **kw: Any):
- self.kw = kw
- @hybridmethod
- def _stateful(self, **kw: Any) -> _stateful_declared_attr[_T]:
- new_kw = self.kw.copy()
- new_kw.update(kw)
- return _stateful_declared_attr(**new_kw)
- def __call__(self, fn: _DeclaredAttrDecorated[_T]) -> declared_attr[_T]:
- return declared_attr(fn, **self.kw)
- def declarative_mixin(cls: Type[_T]) -> Type[_T]:
- """Mark a class as providing the feature of "declarative mixin".
- E.g.::
- from sqlalchemy.orm import declared_attr
- from sqlalchemy.orm import declarative_mixin
- @declarative_mixin
- class MyMixin:
- @declared_attr
- def __tablename__(cls):
- return cls.__name__.lower()
- __table_args__ = {"mysql_engine": "InnoDB"}
- __mapper_args__ = {"always_refresh": True}
- id = Column(Integer, primary_key=True)
- class MyModel(MyMixin, Base):
- name = Column(String(1000))
- The :func:`_orm.declarative_mixin` decorator currently does not modify
- the given class in any way; it's current purpose is strictly to assist
- the :ref:`Mypy plugin <mypy_toplevel>` in being able to identify
- SQLAlchemy declarative mixin classes when no other context is present.
- .. versionadded:: 1.4.6
- .. legacy:: This api is considered legacy and will be deprecated in the next
- SQLAlchemy version.
- .. seealso::
- :ref:`orm_mixins_toplevel`
- :ref:`mypy_declarative_mixins` - in the
- :ref:`Mypy plugin documentation <mypy_toplevel>`
- """ # noqa: E501
- return cls
- def _setup_declarative_base(cls: Type[Any]) -> None:
- if "metadata" in cls.__dict__:
- metadata = cls.__dict__["metadata"]
- else:
- metadata = None
- if "type_annotation_map" in cls.__dict__:
- type_annotation_map = cls.__dict__["type_annotation_map"]
- else:
- type_annotation_map = None
- reg = cls.__dict__.get("registry", None)
- if reg is not None:
- if not isinstance(reg, registry):
- raise exc.InvalidRequestError(
- "Declarative base class has a 'registry' attribute that is "
- "not an instance of sqlalchemy.orm.registry()"
- )
- elif type_annotation_map is not None:
- raise exc.InvalidRequestError(
- "Declarative base class has both a 'registry' attribute and a "
- "type_annotation_map entry. Per-base type_annotation_maps "
- "are not supported. Please apply the type_annotation_map "
- "to this registry directly."
- )
- else:
- reg = registry(
- metadata=metadata, type_annotation_map=type_annotation_map
- )
- cls.registry = reg
- cls._sa_registry = reg
- if "metadata" not in cls.__dict__:
- cls.metadata = cls.registry.metadata
- if getattr(cls, "__init__", object.__init__) is object.__init__:
- cls.__init__ = cls.registry.constructor
- class MappedAsDataclass(metaclass=DCTransformDeclarative):
- """Mixin class to indicate when mapping this class, also convert it to be
- a dataclass.
- .. seealso::
- :ref:`orm_declarative_native_dataclasses` - complete background
- on SQLAlchemy native dataclass mapping
- .. versionadded:: 2.0
- """
- def __init_subclass__(
- cls,
- init: Union[_NoArg, bool] = _NoArg.NO_ARG,
- repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
- eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
- order: Union[_NoArg, bool] = _NoArg.NO_ARG,
- unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
- match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
- kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
- dataclass_callable: Union[
- _NoArg, Callable[..., Type[Any]]
- ] = _NoArg.NO_ARG,
- **kw: Any,
- ) -> None:
- apply_dc_transforms: _DataclassArguments = {
- "init": init,
- "repr": repr,
- "eq": eq,
- "order": order,
- "unsafe_hash": unsafe_hash,
- "match_args": match_args,
- "kw_only": kw_only,
- "dataclass_callable": dataclass_callable,
- }
- current_transforms: _DataclassArguments
- if hasattr(cls, "_sa_apply_dc_transforms"):
- current = cls._sa_apply_dc_transforms
- _ClassScanMapperConfig._assert_dc_arguments(current)
- cls._sa_apply_dc_transforms = current_transforms = { # type: ignore # noqa: E501
- k: current.get(k, _NoArg.NO_ARG) if v is _NoArg.NO_ARG else v
- for k, v in apply_dc_transforms.items()
- }
- else:
- cls._sa_apply_dc_transforms = current_transforms = (
- apply_dc_transforms
- )
- super().__init_subclass__(**kw)
- if not _is_mapped_class(cls):
- new_anno = (
- _ClassScanMapperConfig._update_annotations_for_non_mapped_class
- )(cls)
- _ClassScanMapperConfig._apply_dataclasses_to_any_class(
- current_transforms, cls, new_anno
- )
- class DeclarativeBase(
- # Inspectable is used only by the mypy plugin
- inspection.Inspectable[InstanceState[Any]],
- metaclass=DeclarativeAttributeIntercept,
- ):
- """Base class used for declarative class definitions.
- The :class:`_orm.DeclarativeBase` allows for the creation of new
- declarative bases in such a way that is compatible with type checkers::
- from sqlalchemy.orm import DeclarativeBase
- class Base(DeclarativeBase):
- pass
- The above ``Base`` class is now usable as the base for new declarative
- mappings. The superclass makes use of the ``__init_subclass__()``
- method to set up new classes and metaclasses aren't used.
- When first used, the :class:`_orm.DeclarativeBase` class instantiates a new
- :class:`_orm.registry` to be used with the base, assuming one was not
- provided explicitly. The :class:`_orm.DeclarativeBase` class supports
- class-level attributes which act as parameters for the construction of this
- registry; such as to indicate a specific :class:`_schema.MetaData`
- collection as well as a specific value for
- :paramref:`_orm.registry.type_annotation_map`::
- from typing_extensions import Annotated
- from sqlalchemy import BigInteger
- from sqlalchemy import MetaData
- from sqlalchemy import String
- from sqlalchemy.orm import DeclarativeBase
- bigint = Annotated[int, "bigint"]
- my_metadata = MetaData()
- class Base(DeclarativeBase):
- metadata = my_metadata
- type_annotation_map = {
- str: String().with_variant(String(255), "mysql", "mariadb"),
- bigint: BigInteger(),
- }
- Class-level attributes which may be specified include:
- :param metadata: optional :class:`_schema.MetaData` collection.
- If a :class:`_orm.registry` is constructed automatically, this
- :class:`_schema.MetaData` collection will be used to construct it.
- Otherwise, the local :class:`_schema.MetaData` collection will supercede
- that used by an existing :class:`_orm.registry` passed using the
- :paramref:`_orm.DeclarativeBase.registry` parameter.
- :param type_annotation_map: optional type annotation map that will be
- passed to the :class:`_orm.registry` as
- :paramref:`_orm.registry.type_annotation_map`.
- :param registry: supply a pre-existing :class:`_orm.registry` directly.
- .. versionadded:: 2.0 Added :class:`.DeclarativeBase`, so that declarative
- base classes may be constructed in such a way that is also recognized
- by :pep:`484` type checkers. As a result, :class:`.DeclarativeBase`
- and other subclassing-oriented APIs should be seen as
- superseding previous "class returned by a function" APIs, namely
- :func:`_orm.declarative_base` and :meth:`_orm.registry.generate_base`,
- where the base class returned cannot be recognized by type checkers
- without using plugins.
- **__init__ behavior**
- In a plain Python class, the base-most ``__init__()`` method in the class
- hierarchy is ``object.__init__()``, which accepts no arguments. However,
- when the :class:`_orm.DeclarativeBase` subclass is first declared, the
- class is given an ``__init__()`` method that links to the
- :paramref:`_orm.registry.constructor` constructor function, if no
- ``__init__()`` method is already present; this is the usual declarative
- constructor that will assign keyword arguments as attributes on the
- instance, assuming those attributes are established at the class level
- (i.e. are mapped, or are linked to a descriptor). This constructor is
- **never accessed by a mapped class without being called explicitly via
- super()**, as mapped classes are themselves given an ``__init__()`` method
- directly which calls :paramref:`_orm.registry.constructor`, so in the
- default case works independently of what the base-most ``__init__()``
- method does.
- .. versionchanged:: 2.0.1 :class:`_orm.DeclarativeBase` has a default
- constructor that links to :paramref:`_orm.registry.constructor` by
- default, so that calls to ``super().__init__()`` can access this
- constructor. Previously, due to an implementation mistake, this default
- constructor was missing, and calling ``super().__init__()`` would invoke
- ``object.__init__()``.
- The :class:`_orm.DeclarativeBase` subclass may also declare an explicit
- ``__init__()`` method which will replace the use of the
- :paramref:`_orm.registry.constructor` function at this level::
- class Base(DeclarativeBase):
- def __init__(self, id=None):
- self.id = id
- Mapped classes still will not invoke this constructor implicitly; it
- remains only accessible by calling ``super().__init__()``::
- class MyClass(Base):
- def __init__(self, id=None, name=None):
- self.name = name
- super().__init__(id=id)
- Note that this is a different behavior from what functions like the legacy
- :func:`_orm.declarative_base` would do; the base created by those functions
- would always install :paramref:`_orm.registry.constructor` for
- ``__init__()``.
- """
- if typing.TYPE_CHECKING:
- def _sa_inspect_type(self) -> Mapper[Self]: ...
- def _sa_inspect_instance(self) -> InstanceState[Self]: ...
- _sa_registry: ClassVar[_RegistryType]
- registry: ClassVar[_RegistryType]
- """Refers to the :class:`_orm.registry` in use where new
- :class:`_orm.Mapper` objects will be associated."""
- metadata: ClassVar[MetaData]
- """Refers to the :class:`_schema.MetaData` collection that will be used
- for new :class:`_schema.Table` objects.
- .. seealso::
- :ref:`orm_declarative_metadata`
- """
- __name__: ClassVar[str]
- # this ideally should be Mapper[Self], but mypy as of 1.4.1 does not
- # like it, and breaks the declared_attr_one test. Pyright/pylance is
- # ok with it.
- __mapper__: ClassVar[Mapper[Any]]
- """The :class:`_orm.Mapper` object to which a particular class is
- mapped.
- May also be acquired using :func:`_sa.inspect`, e.g.
- ``inspect(klass)``.
- """
- __table__: ClassVar[FromClause]
- """The :class:`_sql.FromClause` to which a particular subclass is
- mapped.
- This is usually an instance of :class:`_schema.Table` but may also
- refer to other kinds of :class:`_sql.FromClause` such as
- :class:`_sql.Subquery`, depending on how the class is mapped.
- .. seealso::
- :ref:`orm_declarative_metadata`
- """
- # pyright/pylance do not consider a classmethod a ClassVar so use Any
- # https://github.com/microsoft/pylance-release/issues/3484
- __tablename__: Any
- """String name to assign to the generated
- :class:`_schema.Table` object, if not specified directly via
- :attr:`_orm.DeclarativeBase.__table__`.
- .. seealso::
- :ref:`orm_declarative_table`
- """
- __mapper_args__: Any
- """Dictionary of arguments which will be passed to the
- :class:`_orm.Mapper` constructor.
- .. seealso::
- :ref:`orm_declarative_mapper_options`
- """
- __table_args__: Any
- """A dictionary or tuple of arguments that will be passed to the
- :class:`_schema.Table` constructor. See
- :ref:`orm_declarative_table_configuration`
- for background on the specific structure of this collection.
- .. seealso::
- :ref:`orm_declarative_table_configuration`
- """
- def __init__(self, **kw: Any): ...
- def __init_subclass__(cls, **kw: Any) -> None:
- if DeclarativeBase in cls.__bases__:
- _check_not_declarative(cls, DeclarativeBase)
- _setup_declarative_base(cls)
- else:
- _as_declarative(cls._sa_registry, cls, cls.__dict__)
- super().__init_subclass__(**kw)
- def _check_not_declarative(cls: Type[Any], base: Type[Any]) -> None:
- cls_dict = cls.__dict__
- if (
- "__table__" in cls_dict
- and not (
- callable(cls_dict["__table__"])
- or hasattr(cls_dict["__table__"], "__get__")
- )
- ) or isinstance(cls_dict.get("__tablename__", None), str):
- raise exc.InvalidRequestError(
- f"Cannot use {base.__name__!r} directly as a declarative base "
- "class. Create a Base by creating a subclass of it."
- )
- class DeclarativeBaseNoMeta(
- # Inspectable is used only by the mypy plugin
- inspection.Inspectable[InstanceState[Any]]
- ):
- """Same as :class:`_orm.DeclarativeBase`, but does not use a metaclass
- to intercept new attributes.
- The :class:`_orm.DeclarativeBaseNoMeta` base may be used when use of
- custom metaclasses is desirable.
- .. versionadded:: 2.0
- """
- _sa_registry: ClassVar[_RegistryType]
- registry: ClassVar[_RegistryType]
- """Refers to the :class:`_orm.registry` in use where new
- :class:`_orm.Mapper` objects will be associated."""
- metadata: ClassVar[MetaData]
- """Refers to the :class:`_schema.MetaData` collection that will be used
- for new :class:`_schema.Table` objects.
- .. seealso::
- :ref:`orm_declarative_metadata`
- """
- # this ideally should be Mapper[Self], but mypy as of 1.4.1 does not
- # like it, and breaks the declared_attr_one test. Pyright/pylance is
- # ok with it.
- __mapper__: ClassVar[Mapper[Any]]
- """The :class:`_orm.Mapper` object to which a particular class is
- mapped.
- May also be acquired using :func:`_sa.inspect`, e.g.
- ``inspect(klass)``.
- """
- __table__: Optional[FromClause]
- """The :class:`_sql.FromClause` to which a particular subclass is
- mapped.
- This is usually an instance of :class:`_schema.Table` but may also
- refer to other kinds of :class:`_sql.FromClause` such as
- :class:`_sql.Subquery`, depending on how the class is mapped.
- .. seealso::
- :ref:`orm_declarative_metadata`
- """
- if typing.TYPE_CHECKING:
- def _sa_inspect_type(self) -> Mapper[Self]: ...
- def _sa_inspect_instance(self) -> InstanceState[Self]: ...
- __tablename__: Any
- """String name to assign to the generated
- :class:`_schema.Table` object, if not specified directly via
- :attr:`_orm.DeclarativeBase.__table__`.
- .. seealso::
- :ref:`orm_declarative_table`
- """
- __mapper_args__: Any
- """Dictionary of arguments which will be passed to the
- :class:`_orm.Mapper` constructor.
- .. seealso::
- :ref:`orm_declarative_mapper_options`
- """
- __table_args__: Any
- """A dictionary or tuple of arguments that will be passed to the
- :class:`_schema.Table` constructor. See
- :ref:`orm_declarative_table_configuration`
- for background on the specific structure of this collection.
- .. seealso::
- :ref:`orm_declarative_table_configuration`
- """
- def __init__(self, **kw: Any): ...
- def __init_subclass__(cls, **kw: Any) -> None:
- if DeclarativeBaseNoMeta in cls.__bases__:
- _check_not_declarative(cls, DeclarativeBaseNoMeta)
- _setup_declarative_base(cls)
- else:
- _as_declarative(cls._sa_registry, cls, cls.__dict__)
- super().__init_subclass__(**kw)
- def add_mapped_attribute(
- target: Type[_O], key: str, attr: MapperProperty[Any]
- ) -> None:
- """Add a new mapped attribute to an ORM mapped class.
- E.g.::
- add_mapped_attribute(User, "addresses", relationship(Address))
- This may be used for ORM mappings that aren't using a declarative
- metaclass that intercepts attribute set operations.
- .. versionadded:: 2.0
- """
- _add_attribute(target, key, attr)
- def declarative_base(
- *,
- metadata: Optional[MetaData] = None,
- mapper: Optional[Callable[..., Mapper[Any]]] = None,
- cls: Type[Any] = object,
- name: str = "Base",
- class_registry: Optional[clsregistry._ClsRegistryType] = None,
- type_annotation_map: Optional[_TypeAnnotationMapType] = None,
- constructor: Callable[..., None] = _declarative_constructor,
- metaclass: Type[Any] = DeclarativeMeta,
- ) -> Any:
- r"""Construct a base class for declarative class definitions.
- The new base class will be given a metaclass that produces
- appropriate :class:`~sqlalchemy.schema.Table` objects and makes
- the appropriate :class:`_orm.Mapper` calls based on the
- information provided declaratively in the class and any subclasses
- of the class.
- .. versionchanged:: 2.0 Note that the :func:`_orm.declarative_base`
- function is superseded by the new :class:`_orm.DeclarativeBase` class,
- which generates a new "base" class using subclassing, rather than
- return value of a function. This allows an approach that is compatible
- with :pep:`484` typing tools.
- The :func:`_orm.declarative_base` function is a shorthand version
- of using the :meth:`_orm.registry.generate_base`
- method. That is, the following::
- from sqlalchemy.orm import declarative_base
- Base = declarative_base()
- Is equivalent to::
- from sqlalchemy.orm import registry
- mapper_registry = registry()
- Base = mapper_registry.generate_base()
- See the docstring for :class:`_orm.registry`
- and :meth:`_orm.registry.generate_base`
- for more details.
- .. versionchanged:: 1.4 The :func:`_orm.declarative_base`
- function is now a specialization of the more generic
- :class:`_orm.registry` class. The function also moves to the
- ``sqlalchemy.orm`` package from the ``declarative.ext`` package.
- :param metadata:
- An optional :class:`~sqlalchemy.schema.MetaData` instance. All
- :class:`~sqlalchemy.schema.Table` objects implicitly declared by
- subclasses of the base will share this MetaData. A MetaData instance
- will be created if none is provided. The
- :class:`~sqlalchemy.schema.MetaData` instance will be available via the
- ``metadata`` attribute of the generated declarative base class.
- :param mapper:
- An optional callable, defaults to :class:`_orm.Mapper`. Will
- be used to map subclasses to their Tables.
- :param cls:
- Defaults to :class:`object`. A type to use as the base for the generated
- declarative base class. May be a class or tuple of classes.
- :param name:
- Defaults to ``Base``. The display name for the generated
- class. Customizing this is not required, but can improve clarity in
- tracebacks and debugging.
- :param constructor:
- Specify the implementation for the ``__init__`` function on a mapped
- class that has no ``__init__`` of its own. Defaults to an
- implementation that assigns \**kwargs for declared
- fields and relationships to an instance. If ``None`` is supplied,
- no __init__ will be provided and construction will fall back to
- cls.__init__ by way of the normal Python semantics.
- :param class_registry: optional dictionary that will serve as the
- registry of class names-> mapped classes when string names
- are used to identify classes inside of :func:`_orm.relationship`
- and others. Allows two or more declarative base classes
- to share the same registry of class names for simplified
- inter-base relationships.
- :param type_annotation_map: optional dictionary of Python types to
- SQLAlchemy :class:`_types.TypeEngine` classes or instances. This
- is used exclusively by the :class:`_orm.MappedColumn` construct
- to produce column types based on annotations within the
- :class:`_orm.Mapped` type.
- .. versionadded:: 2.0
- .. seealso::
- :ref:`orm_declarative_mapped_column_type_map`
- :param metaclass:
- Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__
- compatible callable to use as the meta type of the generated
- declarative base class.
- .. seealso::
- :class:`_orm.registry`
- """
- return registry(
- metadata=metadata,
- class_registry=class_registry,
- constructor=constructor,
- type_annotation_map=type_annotation_map,
- ).generate_base(
- mapper=mapper,
- cls=cls,
- name=name,
- metaclass=metaclass,
- )
- class registry:
- """Generalized registry for mapping classes.
- The :class:`_orm.registry` serves as the basis for maintaining a collection
- of mappings, and provides configurational hooks used to map classes.
- The three general kinds of mappings supported are Declarative Base,
- Declarative Decorator, and Imperative Mapping. All of these mapping
- styles may be used interchangeably:
- * :meth:`_orm.registry.generate_base` returns a new declarative base
- class, and is the underlying implementation of the
- :func:`_orm.declarative_base` function.
- * :meth:`_orm.registry.mapped` provides a class decorator that will
- apply declarative mapping to a class without the use of a declarative
- base class.
- * :meth:`_orm.registry.map_imperatively` will produce a
- :class:`_orm.Mapper` for a class without scanning the class for
- declarative class attributes. This method suits the use case historically
- provided by the ``sqlalchemy.orm.mapper()`` classical mapping function,
- which is removed as of SQLAlchemy 2.0.
- .. versionadded:: 1.4
- .. seealso::
- :ref:`orm_mapping_classes_toplevel` - overview of class mapping
- styles.
- """
- _class_registry: clsregistry._ClsRegistryType
- _managers: weakref.WeakKeyDictionary[ClassManager[Any], Literal[True]]
- _non_primary_mappers: weakref.WeakKeyDictionary[Mapper[Any], Literal[True]]
- metadata: MetaData
- constructor: CallableReference[Callable[..., None]]
- type_annotation_map: _MutableTypeAnnotationMapType
- _dependents: Set[_RegistryType]
- _dependencies: Set[_RegistryType]
- _new_mappers: bool
- def __init__(
- self,
- *,
- metadata: Optional[MetaData] = None,
- class_registry: Optional[clsregistry._ClsRegistryType] = None,
- type_annotation_map: Optional[_TypeAnnotationMapType] = None,
- constructor: Callable[..., None] = _declarative_constructor,
- ):
- r"""Construct a new :class:`_orm.registry`
- :param metadata:
- An optional :class:`_schema.MetaData` instance. All
- :class:`_schema.Table` objects generated using declarative
- table mapping will make use of this :class:`_schema.MetaData`
- collection. If this argument is left at its default of ``None``,
- a blank :class:`_schema.MetaData` collection is created.
- :param constructor:
- Specify the implementation for the ``__init__`` function on a mapped
- class that has no ``__init__`` of its own. Defaults to an
- implementation that assigns \**kwargs for declared
- fields and relationships to an instance. If ``None`` is supplied,
- no __init__ will be provided and construction will fall back to
- cls.__init__ by way of the normal Python semantics.
- :param class_registry: optional dictionary that will serve as the
- registry of class names-> mapped classes when string names
- are used to identify classes inside of :func:`_orm.relationship`
- and others. Allows two or more declarative base classes
- to share the same registry of class names for simplified
- inter-base relationships.
- :param type_annotation_map: optional dictionary of Python types to
- SQLAlchemy :class:`_types.TypeEngine` classes or instances.
- The provided dict will update the default type mapping. This
- is used exclusively by the :class:`_orm.MappedColumn` construct
- to produce column types based on annotations within the
- :class:`_orm.Mapped` type.
- .. versionadded:: 2.0
- .. seealso::
- :ref:`orm_declarative_mapped_column_type_map`
- """
- lcl_metadata = metadata or MetaData()
- if class_registry is None:
- class_registry = weakref.WeakValueDictionary()
- self._class_registry = class_registry
- self._managers = weakref.WeakKeyDictionary()
- self._non_primary_mappers = weakref.WeakKeyDictionary()
- self.metadata = lcl_metadata
- self.constructor = constructor
- self.type_annotation_map = {}
- if type_annotation_map is not None:
- self.update_type_annotation_map(type_annotation_map)
- self._dependents = set()
- self._dependencies = set()
- self._new_mappers = False
- with mapperlib._CONFIGURE_MUTEX:
- mapperlib._mapper_registries[self] = True
- def update_type_annotation_map(
- self,
- type_annotation_map: _TypeAnnotationMapType,
- ) -> None:
- """update the :paramref:`_orm.registry.type_annotation_map` with new
- values."""
- self.type_annotation_map.update(
- {
- de_optionalize_union_types(typ): sqltype
- for typ, sqltype in type_annotation_map.items()
- }
- )
- def _resolve_type(
- self, python_type: _MatchedOnType, _do_fallbacks: bool = True
- ) -> Optional[sqltypes.TypeEngine[Any]]:
- python_type_type: Type[Any]
- search: Iterable[Tuple[_MatchedOnType, Type[Any]]]
- if is_generic(python_type):
- if is_literal(python_type):
- python_type_type = python_type # type: ignore[assignment]
- search = (
- (python_type, python_type_type),
- *((lt, python_type_type) for lt in LITERAL_TYPES),
- )
- else:
- python_type_type = python_type.__origin__
- search = ((python_type, python_type_type),)
- elif isinstance(python_type, type):
- python_type_type = python_type
- search = ((pt, pt) for pt in python_type_type.__mro__)
- else:
- python_type_type = python_type # type: ignore[assignment]
- search = ((python_type, python_type_type),)
- for pt, flattened in search:
- # we search through full __mro__ for types. however...
- sql_type = self.type_annotation_map.get(pt)
- if sql_type is None:
- sql_type = sqltypes._type_map_get(pt) # type: ignore # noqa: E501
- if sql_type is not None:
- sql_type_inst = sqltypes.to_instance(sql_type)
- # ... this additional step will reject most
- # type -> supertype matches, such as if we had
- # a MyInt(int) subclass. note also we pass NewType()
- # here directly; these always have to be in the
- # type_annotation_map to be useful
- resolved_sql_type = sql_type_inst._resolve_for_python_type(
- python_type_type,
- pt,
- flattened,
- )
- if resolved_sql_type is not None:
- return resolved_sql_type
- # 2.0 fallbacks
- if _do_fallbacks:
- python_type_to_check: Any = None
- kind = None
- if is_pep695(python_type):
- # NOTE: assume there aren't type alias types of new types.
- python_type_to_check = python_type
- while is_pep695(python_type_to_check):
- python_type_to_check = python_type_to_check.__value__
- python_type_to_check = de_optionalize_union_types(
- python_type_to_check
- )
- kind = "TypeAliasType"
- if is_newtype(python_type):
- python_type_to_check = flatten_newtype(python_type)
- kind = "NewType"
- if python_type_to_check is not None:
- res_after_fallback = self._resolve_type(
- python_type_to_check, False
- )
- if res_after_fallback is not None:
- assert kind is not None
- warn_deprecated(
- f"Matching the provided {kind} '{python_type}' on "
- "its resolved value without matching it in the "
- "type_annotation_map is deprecated; add this type to "
- "the type_annotation_map to allow it to match "
- "explicitly.",
- "2.0",
- )
- return res_after_fallback
- return None
- @property
- def mappers(self) -> FrozenSet[Mapper[Any]]:
- """read only collection of all :class:`_orm.Mapper` objects."""
- return frozenset(manager.mapper for manager in self._managers).union(
- self._non_primary_mappers
- )
- def _set_depends_on(self, registry: RegistryType) -> None:
- if registry is self:
- return
- registry._dependents.add(self)
- self._dependencies.add(registry)
- def _flag_new_mapper(self, mapper: Mapper[Any]) -> None:
- mapper._ready_for_configure = True
- if self._new_mappers:
- return
- for reg in self._recurse_with_dependents({self}):
- reg._new_mappers = True
- @classmethod
- def _recurse_with_dependents(
- cls, registries: Set[RegistryType]
- ) -> Iterator[RegistryType]:
- todo = registries
- done = set()
- while todo:
- reg = todo.pop()
- done.add(reg)
- # if yielding would remove dependents, make sure we have
- # them before
- todo.update(reg._dependents.difference(done))
- yield reg
- # if yielding would add dependents, make sure we have them
- # after
- todo.update(reg._dependents.difference(done))
- @classmethod
- def _recurse_with_dependencies(
- cls, registries: Set[RegistryType]
- ) -> Iterator[RegistryType]:
- todo = registries
- done = set()
- while todo:
- reg = todo.pop()
- done.add(reg)
- # if yielding would remove dependencies, make sure we have
- # them before
- todo.update(reg._dependencies.difference(done))
- yield reg
- # if yielding would remove dependencies, make sure we have
- # them before
- todo.update(reg._dependencies.difference(done))
- def _mappers_to_configure(self) -> Iterator[Mapper[Any]]:
- return itertools.chain(
- (
- manager.mapper
- for manager in list(self._managers)
- if manager.is_mapped
- and not manager.mapper.configured
- and manager.mapper._ready_for_configure
- ),
- (
- npm
- for npm in list(self._non_primary_mappers)
- if not npm.configured and npm._ready_for_configure
- ),
- )
- def _add_non_primary_mapper(self, np_mapper: Mapper[Any]) -> None:
- self._non_primary_mappers[np_mapper] = True
- def _dispose_cls(self, cls: Type[_O]) -> None:
- clsregistry.remove_class(cls.__name__, cls, self._class_registry)
- def _add_manager(self, manager: ClassManager[Any]) -> None:
- self._managers[manager] = True
- if manager.is_mapped:
- raise exc.ArgumentError(
- "Class '%s' already has a primary mapper defined. "
- % manager.class_
- )
- assert manager.registry is None
- manager.registry = self
- def configure(self, cascade: bool = False) -> None:
- """Configure all as-yet unconfigured mappers in this
- :class:`_orm.registry`.
- The configure step is used to reconcile and initialize the
- :func:`_orm.relationship` linkages between mapped classes, as well as
- to invoke configuration events such as the
- :meth:`_orm.MapperEvents.before_configured` and
- :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM
- extensions or user-defined extension hooks.
- If one or more mappers in this registry contain
- :func:`_orm.relationship` constructs that refer to mapped classes in
- other registries, this registry is said to be *dependent* on those
- registries. In order to configure those dependent registries
- automatically, the :paramref:`_orm.registry.configure.cascade` flag
- should be set to ``True``. Otherwise, if they are not configured, an
- exception will be raised. The rationale behind this behavior is to
- allow an application to programmatically invoke configuration of
- registries while controlling whether or not the process implicitly
- reaches other registries.
- As an alternative to invoking :meth:`_orm.registry.configure`, the ORM
- function :func:`_orm.configure_mappers` function may be used to ensure
- configuration is complete for all :class:`_orm.registry` objects in
- memory. This is generally simpler to use and also predates the usage of
- :class:`_orm.registry` objects overall. However, this function will
- impact all mappings throughout the running Python process and may be
- more memory/time consuming for an application that has many registries
- in use for different purposes that may not be needed immediately.
- .. seealso::
- :func:`_orm.configure_mappers`
- .. versionadded:: 1.4.0b2
- """
- mapperlib._configure_registries({self}, cascade=cascade)
- def dispose(self, cascade: bool = False) -> None:
- """Dispose of all mappers in this :class:`_orm.registry`.
- After invocation, all the classes that were mapped within this registry
- will no longer have class instrumentation associated with them. This
- method is the per-:class:`_orm.registry` analogue to the
- application-wide :func:`_orm.clear_mappers` function.
- If this registry contains mappers that are dependencies of other
- registries, typically via :func:`_orm.relationship` links, then those
- registries must be disposed as well. When such registries exist in
- relation to this one, their :meth:`_orm.registry.dispose` method will
- also be called, if the :paramref:`_orm.registry.dispose.cascade` flag
- is set to ``True``; otherwise, an error is raised if those registries
- were not already disposed.
- .. versionadded:: 1.4.0b2
- .. seealso::
- :func:`_orm.clear_mappers`
- """
- mapperlib._dispose_registries({self}, cascade=cascade)
- def _dispose_manager_and_mapper(self, manager: ClassManager[Any]) -> None:
- if "mapper" in manager.__dict__:
- mapper = manager.mapper
- mapper._set_dispose_flags()
- class_ = manager.class_
- self._dispose_cls(class_)
- instrumentation._instrumentation_factory.unregister(class_)
- def generate_base(
- self,
- mapper: Optional[Callable[..., Mapper[Any]]] = None,
- cls: Type[Any] = object,
- name: str = "Base",
- metaclass: Type[Any] = DeclarativeMeta,
- ) -> Any:
- """Generate a declarative base class.
- Classes that inherit from the returned class object will be
- automatically mapped using declarative mapping.
- E.g.::
- from sqlalchemy.orm import registry
- mapper_registry = registry()
- Base = mapper_registry.generate_base()
- class MyClass(Base):
- __tablename__ = "my_table"
- id = Column(Integer, primary_key=True)
- The above dynamically generated class is equivalent to the
- non-dynamic example below::
- from sqlalchemy.orm import registry
- from sqlalchemy.orm.decl_api import DeclarativeMeta
- mapper_registry = registry()
- class Base(metaclass=DeclarativeMeta):
- __abstract__ = True
- registry = mapper_registry
- metadata = mapper_registry.metadata
- __init__ = mapper_registry.constructor
- .. versionchanged:: 2.0 Note that the
- :meth:`_orm.registry.generate_base` method is superseded by the new
- :class:`_orm.DeclarativeBase` class, which generates a new "base"
- class using subclassing, rather than return value of a function.
- This allows an approach that is compatible with :pep:`484` typing
- tools.
- The :meth:`_orm.registry.generate_base` method provides the
- implementation for the :func:`_orm.declarative_base` function, which
- creates the :class:`_orm.registry` and base class all at once.
- See the section :ref:`orm_declarative_mapping` for background and
- examples.
- :param mapper:
- An optional callable, defaults to :class:`_orm.Mapper`.
- This function is used to generate new :class:`_orm.Mapper` objects.
- :param cls:
- Defaults to :class:`object`. A type to use as the base for the
- generated declarative base class. May be a class or tuple of classes.
- :param name:
- Defaults to ``Base``. The display name for the generated
- class. Customizing this is not required, but can improve clarity in
- tracebacks and debugging.
- :param metaclass:
- Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__
- compatible callable to use as the meta type of the generated
- declarative base class.
- .. seealso::
- :ref:`orm_declarative_mapping`
- :func:`_orm.declarative_base`
- """
- metadata = self.metadata
- bases = not isinstance(cls, tuple) and (cls,) or cls
- class_dict: Dict[str, Any] = dict(registry=self, metadata=metadata)
- if isinstance(cls, type):
- class_dict["__doc__"] = cls.__doc__
- if self.constructor is not None:
- class_dict["__init__"] = self.constructor
- class_dict["__abstract__"] = True
- if mapper:
- class_dict["__mapper_cls__"] = mapper
- if hasattr(cls, "__class_getitem__"):
- def __class_getitem__(cls: Type[_T], key: Any) -> Type[_T]:
- # allow generic classes in py3.9+
- return cls
- class_dict["__class_getitem__"] = __class_getitem__
- return metaclass(name, bases, class_dict)
- @compat_typing.dataclass_transform(
- field_specifiers=(
- MappedColumn,
- RelationshipProperty,
- Composite,
- Synonym,
- mapped_column,
- relationship,
- composite,
- synonym,
- deferred,
- ),
- )
- @overload
- def mapped_as_dataclass(self, __cls: Type[_O]) -> Type[_O]: ...
- @overload
- def mapped_as_dataclass(
- self,
- __cls: Literal[None] = ...,
- *,
- init: Union[_NoArg, bool] = ...,
- repr: Union[_NoArg, bool] = ..., # noqa: A002
- eq: Union[_NoArg, bool] = ...,
- order: Union[_NoArg, bool] = ...,
- unsafe_hash: Union[_NoArg, bool] = ...,
- match_args: Union[_NoArg, bool] = ...,
- kw_only: Union[_NoArg, bool] = ...,
- dataclass_callable: Union[_NoArg, Callable[..., Type[Any]]] = ...,
- ) -> Callable[[Type[_O]], Type[_O]]: ...
- def mapped_as_dataclass(
- self,
- __cls: Optional[Type[_O]] = None,
- *,
- init: Union[_NoArg, bool] = _NoArg.NO_ARG,
- repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
- eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
- order: Union[_NoArg, bool] = _NoArg.NO_ARG,
- unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
- match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
- kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
- dataclass_callable: Union[
- _NoArg, Callable[..., Type[Any]]
- ] = _NoArg.NO_ARG,
- ) -> Union[Type[_O], Callable[[Type[_O]], Type[_O]]]:
- """Class decorator that will apply the Declarative mapping process
- to a given class, and additionally convert the class to be a
- Python dataclass.
- .. seealso::
- :ref:`orm_declarative_native_dataclasses` - complete background
- on SQLAlchemy native dataclass mapping
- .. versionadded:: 2.0
- """
- def decorate(cls: Type[_O]) -> Type[_O]:
- setattr(
- cls,
- "_sa_apply_dc_transforms",
- {
- "init": init,
- "repr": repr,
- "eq": eq,
- "order": order,
- "unsafe_hash": unsafe_hash,
- "match_args": match_args,
- "kw_only": kw_only,
- "dataclass_callable": dataclass_callable,
- },
- )
- _as_declarative(self, cls, cls.__dict__)
- return cls
- if __cls:
- return decorate(__cls)
- else:
- return decorate
- def mapped(self, cls: Type[_O]) -> Type[_O]:
- """Class decorator that will apply the Declarative mapping process
- to a given class.
- E.g.::
- from sqlalchemy.orm import registry
- mapper_registry = registry()
- @mapper_registry.mapped
- class Foo:
- __tablename__ = "some_table"
- id = Column(Integer, primary_key=True)
- name = Column(String)
- See the section :ref:`orm_declarative_mapping` for complete
- details and examples.
- :param cls: class to be mapped.
- :return: the class that was passed.
- .. seealso::
- :ref:`orm_declarative_mapping`
- :meth:`_orm.registry.generate_base` - generates a base class
- that will apply Declarative mapping to subclasses automatically
- using a Python metaclass.
- .. seealso::
- :meth:`_orm.registry.mapped_as_dataclass`
- """
- _as_declarative(self, cls, cls.__dict__)
- return cls
- def as_declarative_base(self, **kw: Any) -> Callable[[Type[_T]], Type[_T]]:
- """
- Class decorator which will invoke
- :meth:`_orm.registry.generate_base`
- for a given base class.
- E.g.::
- from sqlalchemy.orm import registry
- mapper_registry = registry()
- @mapper_registry.as_declarative_base()
- class Base:
- @declared_attr
- def __tablename__(cls):
- return cls.__name__.lower()
- id = Column(Integer, primary_key=True)
- class MyMappedClass(Base): ...
- All keyword arguments passed to
- :meth:`_orm.registry.as_declarative_base` are passed
- along to :meth:`_orm.registry.generate_base`.
- """
- def decorate(cls: Type[_T]) -> Type[_T]:
- kw["cls"] = cls
- kw["name"] = cls.__name__
- return self.generate_base(**kw) # type: ignore
- return decorate
- def map_declaratively(self, cls: Type[_O]) -> Mapper[_O]:
- """Map a class declaratively.
- In this form of mapping, the class is scanned for mapping information,
- including for columns to be associated with a table, and/or an
- actual table object.
- Returns the :class:`_orm.Mapper` object.
- E.g.::
- from sqlalchemy.orm import registry
- mapper_registry = registry()
- class Foo:
- __tablename__ = "some_table"
- id = Column(Integer, primary_key=True)
- name = Column(String)
- mapper = mapper_registry.map_declaratively(Foo)
- This function is more conveniently invoked indirectly via either the
- :meth:`_orm.registry.mapped` class decorator or by subclassing a
- declarative metaclass generated from
- :meth:`_orm.registry.generate_base`.
- See the section :ref:`orm_declarative_mapping` for complete
- details and examples.
- :param cls: class to be mapped.
- :return: a :class:`_orm.Mapper` object.
- .. seealso::
- :ref:`orm_declarative_mapping`
- :meth:`_orm.registry.mapped` - more common decorator interface
- to this function.
- :meth:`_orm.registry.map_imperatively`
- """
- _as_declarative(self, cls, cls.__dict__)
- return cls.__mapper__ # type: ignore
- def map_imperatively(
- self,
- class_: Type[_O],
- local_table: Optional[FromClause] = None,
- **kw: Any,
- ) -> Mapper[_O]:
- r"""Map a class imperatively.
- In this form of mapping, the class is not scanned for any mapping
- information. Instead, all mapping constructs are passed as
- arguments.
- This method is intended to be fully equivalent to the now-removed
- SQLAlchemy ``mapper()`` function, except that it's in terms of
- a particular registry.
- E.g.::
- from sqlalchemy.orm import registry
- mapper_registry = registry()
- my_table = Table(
- "my_table",
- mapper_registry.metadata,
- Column("id", Integer, primary_key=True),
- )
- class MyClass:
- pass
- mapper_registry.map_imperatively(MyClass, my_table)
- See the section :ref:`orm_imperative_mapping` for complete background
- and usage examples.
- :param class\_: The class to be mapped. Corresponds to the
- :paramref:`_orm.Mapper.class_` parameter.
- :param local_table: the :class:`_schema.Table` or other
- :class:`_sql.FromClause` object that is the subject of the mapping.
- Corresponds to the
- :paramref:`_orm.Mapper.local_table` parameter.
- :param \**kw: all other keyword arguments are passed to the
- :class:`_orm.Mapper` constructor directly.
- .. seealso::
- :ref:`orm_imperative_mapping`
- :ref:`orm_declarative_mapping`
- """
- return _mapper(self, class_, local_table, kw)
- RegistryType = registry
- if not TYPE_CHECKING:
- # allow for runtime type resolution of ``ClassVar[_RegistryType]``
- _RegistryType = registry # noqa
- def as_declarative(**kw: Any) -> Callable[[Type[_T]], Type[_T]]:
- """
- Class decorator which will adapt a given class into a
- :func:`_orm.declarative_base`.
- This function makes use of the :meth:`_orm.registry.as_declarative_base`
- method, by first creating a :class:`_orm.registry` automatically
- and then invoking the decorator.
- E.g.::
- from sqlalchemy.orm import as_declarative
- @as_declarative()
- class Base:
- @declared_attr
- def __tablename__(cls):
- return cls.__name__.lower()
- id = Column(Integer, primary_key=True)
- class MyMappedClass(Base): ...
- .. seealso::
- :meth:`_orm.registry.as_declarative_base`
- """
- metadata, class_registry = (
- kw.pop("metadata", None),
- kw.pop("class_registry", None),
- )
- return registry(
- metadata=metadata, class_registry=class_registry
- ).as_declarative_base(**kw)
- @inspection._inspects(
- DeclarativeMeta, DeclarativeBase, DeclarativeAttributeIntercept
- )
- def _inspect_decl_meta(cls: Type[Any]) -> Optional[Mapper[Any]]:
- mp: Optional[Mapper[Any]] = _inspect_mapped_class(cls)
- if mp is None:
- if _DeferredMapperConfig.has_cls(cls):
- _DeferredMapperConfig.raise_unmapped_for_cls(cls)
- return mp
|