codekingpro/portable-devtools
115k
1# orm/decl_api.py
2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8"""Public API functions and helpers for declarative."""
9
10from __future__ import annotations
11
12import itertools
13import re
14import typing
15from typing import Any
16from typing import Callable
17from typing import cast
18from typing import ClassVar
19from typing import Dict
20from typing import FrozenSet
21from typing import Generic
22from typing import Iterable
23from typing import Iterator
24from typing import Mapping
25from typing import Optional
26from typing import overload
27from typing import Set
28from typing import Tuple
29from typing import Type
30from typing import TYPE_CHECKING
31from typing import TypeVar
32from typing import Union
33import weakref
34
35from . import attributes
36from . import clsregistry
37from . import instrumentation
38from . import interfaces
39from . import mapperlib
40from ._orm_constructors import composite
41from ._orm_constructors import deferred
42from ._orm_constructors import mapped_column
43from ._orm_constructors import relationship
44from ._orm_constructors import synonym
45from .attributes import InstrumentedAttribute
46from .base import _inspect_mapped_class
47from .base import _is_mapped_class
48from .base import Mapped
49from .base import ORMDescriptor
50from .decl_base import _add_attribute
51from .decl_base import _as_declarative
52from .decl_base import _ClassScanMapperConfig
53from .decl_base import _declarative_constructor
54from .decl_base import _DeferredMapperConfig
55from .decl_base import _del_attribute
56from .decl_base import _mapper
57from .descriptor_props import Composite
58from .descriptor_props import Synonym
59from .descriptor_props import Synonym as _orm_synonym
60from .mapper import Mapper
61from .properties import MappedColumn
62from .relationships import RelationshipProperty
63from .state import InstanceState
64from .. import exc
65from .. import inspection
66from .. import util
67from ..sql import sqltypes
68from ..sql.base import _NoArg
69from ..sql.elements import SQLCoreOperations
70from ..sql.schema import MetaData
71from ..sql.selectable import FromClause
72from ..util import hybridmethod
73from ..util import hybridproperty
74from ..util import typing as compat_typing
75from ..util.typing import CallableReference
76from ..util.typing import flatten_newtype
77from ..util.typing import is_generic
78from ..util.typing import is_literal
79from ..util.typing import is_newtype
80from ..util.typing import is_pep695
81from ..util.typing import Literal
82from ..util.typing import Self
83
84if TYPE_CHECKING:
85 from ._typing import _O
86 from ._typing import _RegistryType
87 from .decl_base import _DataclassArguments
88 from .instrumentation import ClassManager
89 from .interfaces import MapperProperty
90 from .state import InstanceState # noqa
91 from ..sql._typing import _TypeEngineArgument
92 from ..sql.type_api import _MatchedOnType
93
94_T = TypeVar("_T", bound=Any)
95
96_TT = TypeVar("_TT", bound=Any)
97
98# it's not clear how to have Annotated, Union objects etc. as keys here
99# from a typing perspective so just leave it open ended for now
100_TypeAnnotationMapType = Mapping[Any, "_TypeEngineArgument[Any]"]
101_MutableTypeAnnotationMapType = Dict[Any, "_TypeEngineArgument[Any]"]
102
103_DeclaredAttrDecorated = Callable[
104 ..., Union[Mapped[_T], ORMDescriptor[_T], SQLCoreOperations[_T]]
105]
106
107
108def has_inherited_table(cls: Type[_O]) -> bool:
109 """Given a class, return True if any of the classes it inherits from has a
110 mapped table, otherwise return False.
111
112 This is used in declarative mixins to build attributes that behave
113 differently for the base class vs. a subclass in an inheritance
114 hierarchy.
115
116 .. seealso::
117
118 :ref:`decl_mixin_inheritance`
119
120 """
121 for class_ in cls.__mro__[1:]:
122 if getattr(class_, "__table__", None) is not None:
123 return True
124 return False
125
126
127class _DynamicAttributesType(type):
128 def __setattr__(cls, key: str, value: Any) -> None:
129 if "__mapper__" in cls.__dict__:
130 _add_attribute(cls, key, value)
131 else:
132 type.__setattr__(cls, key, value)
133
134 def __delattr__(cls, key: str) -> None:
135 if "__mapper__" in cls.__dict__:
136 _del_attribute(cls, key)
137 else:
138 type.__delattr__(cls, key)
139
140
141class DeclarativeAttributeIntercept(
142 _DynamicAttributesType,
143 # Inspectable is used only by the mypy plugin
144 inspection.Inspectable[Mapper[Any]],
145):
146 """Metaclass that may be used in conjunction with the
147 :class:`_orm.DeclarativeBase` class to support addition of class
148 attributes dynamically.
149
150 """
151
152
153@compat_typing.dataclass_transform(
154 field_specifiers=(
155 MappedColumn,
156 RelationshipProperty,
157 Composite,
158 Synonym,
159 mapped_column,
160 relationship,
161 composite,
162 synonym,
163 deferred,
164 ),
165)
166class DCTransformDeclarative(DeclarativeAttributeIntercept):
167 """metaclass that includes @dataclass_transforms"""
168
169
170class DeclarativeMeta(DeclarativeAttributeIntercept):
171 metadata: MetaData
172 registry: RegistryType
173
174 def __init__(
175 cls, classname: Any, bases: Any, dict_: Any, **kw: Any
176 ) -> None:
177 # use cls.__dict__, which can be modified by an
178 # __init_subclass__() method (#7900)
179 dict_ = cls.__dict__
180
181 # early-consume registry from the initial declarative base,
182 # assign privately to not conflict with subclass attributes named
183 # "registry"
184 reg = getattr(cls, "_sa_registry", None)
185 if reg is None:
186 reg = dict_.get("registry", None)
187 if not isinstance(reg, registry):
188 raise exc.InvalidRequestError(
189 "Declarative base class has no 'registry' attribute, "
190 "or registry is not a sqlalchemy.orm.registry() object"
191 )
192 else:
193 cls._sa_registry = reg
194
195 if not cls.__dict__.get("__abstract__", False):
196 _as_declarative(reg, cls, dict_)
197 type.__init__(cls, classname, bases, dict_)
198
199
200def synonym_for(
201 name: str, map_column: bool = False
202) -> Callable[[Callable[..., Any]], Synonym[Any]]:
203 """Decorator that produces an :func:`_orm.synonym`
204 attribute in conjunction with a Python descriptor.
205
206 The function being decorated is passed to :func:`_orm.synonym` as the
207 :paramref:`.orm.synonym.descriptor` parameter::
208
209 class MyClass(Base):
210 __tablename__ = 'my_table'
211
212 id = Column(Integer, primary_key=True)
213 _job_status = Column("job_status", String(50))
214
215 @synonym_for("job_status")
216 @property
217 def job_status(self):
218 return "Status: %s" % self._job_status
219
220 The :ref:`hybrid properties <mapper_hybrids>` feature of SQLAlchemy
221 is typically preferred instead of synonyms, which is a more legacy
222 feature.
223
224 .. seealso::
225
226 :ref:`synonyms` - Overview of synonyms
227
228 :func:`_orm.synonym` - the mapper-level function
229
230 :ref:`mapper_hybrids` - The Hybrid Attribute extension provides an
231 updated approach to augmenting attribute behavior more flexibly than
232 can be achieved with synonyms.
233
234 """
235
236 def decorate(fn: Callable[..., Any]) -> Synonym[Any]:
237 return _orm_synonym(name, map_column=map_column, descriptor=fn)
238
239 return decorate
240
241
242class _declared_attr_common:
243 def __init__(
244 self,
245 fn: Callable[..., Any],
246 cascading: bool = False,
247 quiet: bool = False,
248 ):
249 # suppport
250 # @declared_attr
251 # @classmethod
252 # def foo(cls) -> Mapped[thing]:
253 # ...
254 # which seems to help typing tools interpret the fn as a classmethod
255 # for situations where needed
256 if isinstance(fn, classmethod):
257 fn = fn.__func__
258
259 self.fget = fn
260 self._cascading = cascading
261 self._quiet = quiet
262 self.__doc__ = fn.__doc__
263
264 def _collect_return_annotation(self) -> Optional[Type[Any]]:
265 return util.get_annotations(self.fget).get("return")
266
267 def __get__(self, instance: Optional[object], owner: Any) -> Any:
268 # the declared_attr needs to make use of a cache that exists
269 # for the span of the declarative scan_attributes() phase.
270 # to achieve this we look at the class manager that's configured.
271
272 # note this method should not be called outside of the declarative
273 # setup phase
274
275 cls = owner
276 manager = attributes.opt_manager_of_class(cls)
277 if manager is None:
278 if not re.match(r"^__.+__$", self.fget.__name__):
279 # if there is no manager at all, then this class hasn't been
280 # run through declarative or mapper() at all, emit a warning.
281 util.warn(
282 "Unmanaged access of declarative attribute %s from "
283 "non-mapped class %s" % (self.fget.__name__, cls.__name__)
284 )
285 return self.fget(cls)
286 elif manager.is_mapped:
287 # the class is mapped, which means we're outside of the declarative
288 # scan setup, just run the function.
289 return self.fget(cls)
290
291 # here, we are inside of the declarative scan. use the registry
292 # that is tracking the values of these attributes.
293 declarative_scan = manager.declarative_scan()
294
295 # assert that we are in fact in the declarative scan
296 assert declarative_scan is not None
297
298 reg = declarative_scan.declared_attr_reg
299
300 if self in reg:
301 return reg[self]
302 else:
303 reg[self] = obj = self.fget(cls)
304 return obj
305
306
307class _declared_directive(_declared_attr_common, Generic[_T]):
308 # see mapping_api.rst for docstring
309
310 if typing.TYPE_CHECKING:
311
312 def __init__(
313 self,
314 fn: Callable[..., _T],
315 cascading: bool = False,
316 ): ...
317
318 def __get__(self, instance: Optional[object], owner: Any) -> _T: ...
319
320 def __set__(self, instance: Any, value: Any) -> None: ...
321
322 def __delete__(self, instance: Any) -> None: ...
323
324 def __call__(self, fn: Callable[..., _TT]) -> _declared_directive[_TT]:
325 # extensive fooling of mypy underway...
326 ...
327
328
329class declared_attr(interfaces._MappedAttribute[_T], _declared_attr_common):
330 """Mark a class-level method as representing the definition of
331 a mapped property or Declarative directive.
332
333 :class:`_orm.declared_attr` is typically applied as a decorator to a class
334 level method, turning the attribute into a scalar-like property that can be
335 invoked from the uninstantiated class. The Declarative mapping process
336 looks for these :class:`_orm.declared_attr` callables as it scans classes,
337 and assumes any attribute marked with :class:`_orm.declared_attr` will be a
338 callable that will produce an object specific to the Declarative mapping or
339 table configuration.
340
341 :class:`_orm.declared_attr` is usually applicable to
342 :ref:`mixins <orm_mixins_toplevel>`, to define relationships that are to be
343 applied to different implementors of the class. It may also be used to
344 define dynamically generated column expressions and other Declarative
345 attributes.
346
347 Example::
348
349 class ProvidesUserMixin:
350 "A mixin that adds a 'user' relationship to classes."
351
352 user_id: Mapped[int] = mapped_column(ForeignKey("user_table.id"))
353
354 @declared_attr
355 def user(cls) -> Mapped["User"]:
356 return relationship("User")
357
358 When used with Declarative directives such as ``__tablename__``, the
359 :meth:`_orm.declared_attr.directive` modifier may be used which indicates
360 to :pep:`484` typing tools that the given method is not dealing with
361 :class:`_orm.Mapped` attributes::
362
363 class CreateTableName:
364 @declared_attr.directive
365 def __tablename__(cls) -> str:
366 return cls.__name__.lower()
367
368 :class:`_orm.declared_attr` can also be applied directly to mapped
369 classes, to allow for attributes that dynamically configure themselves
370 on subclasses when using mapped inheritance schemes. Below
371 illustrates :class:`_orm.declared_attr` to create a dynamic scheme
372 for generating the :paramref:`_orm.Mapper.polymorphic_identity` parameter
373 for subclasses::
374
375 class Employee(Base):
376 __tablename__ = 'employee'
377
378 id: Mapped[int] = mapped_column(primary_key=True)
379 type: Mapped[str] = mapped_column(String(50))
380
381 @declared_attr.directive
382 def __mapper_args__(cls) -> Dict[str, Any]:
383 if cls.__name__ == 'Employee':
384 return {
385 "polymorphic_on":cls.type,
386 "polymorphic_identity":"Employee"
387 }
388 else:
389 return {"polymorphic_identity":cls.__name__}
390
391 class Engineer(Employee):
392 pass
393
394 :class:`_orm.declared_attr` supports decorating functions that are
395 explicitly decorated with ``@classmethod``. This is never necessary from a
396 runtime perspective, however may be needed in order to support :pep:`484`
397 typing tools that don't otherwise recognize the decorated function as
398 having class-level behaviors for the ``cls`` parameter::
399
400 class SomethingMixin:
401 x: Mapped[int]
402 y: Mapped[int]
403
404 @declared_attr
405 @classmethod
406 def x_plus_y(cls) -> Mapped[int]:
407 return column_property(cls.x + cls.y)
408
409 .. versionadded:: 2.0 - :class:`_orm.declared_attr` can accommodate a
410 function decorated with ``@classmethod`` to help with :pep:`484`
411 integration where needed.
412
413
414 .. seealso::
415
416 :ref:`orm_mixins_toplevel` - Declarative Mixin documentation with
417 background on use patterns for :class:`_orm.declared_attr`.
418
419 """ # noqa: E501
420
421 if typing.TYPE_CHECKING:
422
423 def __init__(
424 self,
425 fn: _DeclaredAttrDecorated[_T],
426 cascading: bool = False,
427 ): ...
428
429 def __set__(self, instance: Any, value: Any) -> None: ...
430
431 def __delete__(self, instance: Any) -> None: ...
432
433 # this is the Mapped[] API where at class descriptor get time we want
434 # the type checker to see InstrumentedAttribute[_T]. However the
435 # callable function prior to mapping in fact calls the given
436 # declarative function that does not return InstrumentedAttribute
437 @overload
438 def __get__(
439 self, instance: None, owner: Any
440 ) -> InstrumentedAttribute[_T]: ...
441
442 @overload
443 def __get__(self, instance: object, owner: Any) -> _T: ...
444
445 def __get__(
446 self, instance: Optional[object], owner: Any
447 ) -> Union[InstrumentedAttribute[_T], _T]: ...
448
449 @hybridmethod
450 def _stateful(cls, **kw: Any) -> _stateful_declared_attr[_T]:
451 return _stateful_declared_attr(**kw)
452
453 @hybridproperty
454 def directive(cls) -> _declared_directive[Any]:
455 # see mapping_api.rst for docstring
456 return _declared_directive # type: ignore
457
458 @hybridproperty
459 def cascading(cls) -> _stateful_declared_attr[_T]:
460 # see mapping_api.rst for docstring
461 return cls._stateful(cascading=True)
462
463
464class _stateful_declared_attr(declared_attr[_T]):
465 kw: Dict[str, Any]
466
467 def __init__(self, **kw: Any):
468 self.kw = kw
469
470 @hybridmethod
471 def _stateful(self, **kw: Any) -> _stateful_declared_attr[_T]:
472 new_kw = self.kw.copy()
473 new_kw.update(kw)
474 return _stateful_declared_attr(**new_kw)
475
476 def __call__(self, fn: _DeclaredAttrDecorated[_T]) -> declared_attr[_T]:
477 return declared_attr(fn, **self.kw)
478
479
480def declarative_mixin(cls: Type[_T]) -> Type[_T]:
481 """Mark a class as providing the feature of "declarative mixin".
482
483 E.g.::
484
485 from sqlalchemy.orm import declared_attr
486 from sqlalchemy.orm import declarative_mixin
487
488 @declarative_mixin
489 class MyMixin:
490
491 @declared_attr
492 def __tablename__(cls):
493 return cls.__name__.lower()
494
495 __table_args__ = {'mysql_engine': 'InnoDB'}
496 __mapper_args__= {'always_refresh': True}
497
498 id = Column(Integer, primary_key=True)
499
500 class MyModel(MyMixin, Base):
501 name = Column(String(1000))
502
503 The :func:`_orm.declarative_mixin` decorator currently does not modify
504 the given class in any way; it's current purpose is strictly to assist
505 the :ref:`Mypy plugin <mypy_toplevel>` in being able to identify
506 SQLAlchemy declarative mixin classes when no other context is present.
507
508 .. versionadded:: 1.4.6
509
510 .. seealso::
511
512 :ref:`orm_mixins_toplevel`
513
514 :ref:`mypy_declarative_mixins` - in the
515 :ref:`Mypy plugin documentation <mypy_toplevel>`
516
517 """ # noqa: E501
518
519 return cls
520
521
522def _setup_declarative_base(cls: Type[Any]) -> None:
523 if "metadata" in cls.__dict__:
524 metadata = cls.__dict__["metadata"]
525 else:
526 metadata = None
527
528 if "type_annotation_map" in cls.__dict__:
529 type_annotation_map = cls.__dict__["type_annotation_map"]
530 else:
531 type_annotation_map = None
532
533 reg = cls.__dict__.get("registry", None)
534 if reg is not None:
535 if not isinstance(reg, registry):
536 raise exc.InvalidRequestError(
537 "Declarative base class has a 'registry' attribute that is "
538 "not an instance of sqlalchemy.orm.registry()"
539 )
540 elif type_annotation_map is not None:
541 raise exc.InvalidRequestError(
542 "Declarative base class has both a 'registry' attribute and a "
543 "type_annotation_map entry. Per-base type_annotation_maps "
544 "are not supported. Please apply the type_annotation_map "
545 "to this registry directly."
546 )
547
548 else:
549 reg = registry(
550 metadata=metadata, type_annotation_map=type_annotation_map
551 )
552 cls.registry = reg
553
554 cls._sa_registry = reg
555
556 if "metadata" not in cls.__dict__:
557 cls.metadata = cls.registry.metadata
558
559 if getattr(cls, "__init__", object.__init__) is object.__init__:
560 cls.__init__ = cls.registry.constructor
561
562
563class MappedAsDataclass(metaclass=DCTransformDeclarative):
564 """Mixin class to indicate when mapping this class, also convert it to be
565 a dataclass.
566
567 .. seealso::
568
569 :ref:`orm_declarative_native_dataclasses` - complete background
570 on SQLAlchemy native dataclass mapping
571
572 .. versionadded:: 2.0
573
574 """
575
576 def __init_subclass__(
577 cls,
578 init: Union[_NoArg, bool] = _NoArg.NO_ARG,
579 repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
580 eq: Union[_NoArg, bool] = _NoArg.NO_ARG,
581 order: Union[_NoArg, bool] = _NoArg.NO_ARG,
582 unsafe_hash: Union[_NoArg, bool] = _NoArg.NO_ARG,
583 match_args: Union[_NoArg, bool] = _NoArg.NO_ARG,
584 kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
585 dataclass_callable: Union[
586 _NoArg, Callable[..., Type[Any]]
587 ] = _NoArg.NO_ARG,
588 **kw: Any,
589 ) -> None:
590 apply_dc_transforms: _DataclassArguments = {
591 "init": init,
592 "repr": repr,
593 "eq": eq,
594 "order": order,
595 "unsafe_hash": unsafe_hash,
596 "match_args": match_args,
597 "kw_only": kw_only,
598 "dataclass_callable": dataclass_callable,
599 }
600
601 current_transforms: _DataclassArguments
602
603 if hasattr(cls, "_sa_apply_dc_transforms"):
604 current = cls._sa_apply_dc_transforms
605
606 _ClassScanMapperConfig._assert_dc_arguments(current)
607
608 cls._sa_apply_dc_transforms = current_transforms = { # type: ignore # noqa: E501
609 k: current.get(k, _NoArg.NO_ARG) if v is _NoArg.NO_ARG else v
610 for k, v in apply_dc_transforms.items()
611 }
612 else:
613 cls._sa_apply_dc_transforms = current_transforms = (
614 apply_dc_transforms
615 )
616
617 super().__init_subclass__(**kw)
618
619 if not _is_mapped_class(cls):
620 new_anno = (
621 _ClassScanMapperConfig._update_annotations_for_non_mapped_class
622 )(cls)
623 _ClassScanMapperConfig._apply_dataclasses_to_any_class(
624 current_transforms, cls, new_anno
625 )
626
627
628class DeclarativeBase(
629 # Inspectable is used only by the mypy plugin
630 inspection.Inspectable[InstanceState[Any]],
631 metaclass=DeclarativeAttributeIntercept,
632):
633 """Base class used for declarative class definitions.
634
635 The :class:`_orm.DeclarativeBase` allows for the creation of new
636 declarative bases in such a way that is compatible with type checkers::
637
638
639 from sqlalchemy.orm import DeclarativeBase
640
641 class Base(DeclarativeBase):
642 pass
643
644
645 The above ``Base`` class is now usable as the base for new declarative
646 mappings. The superclass makes use of the ``__init_subclass__()``
647 method to set up new classes and metaclasses aren't used.
648
649 When first used, the :class:`_orm.DeclarativeBase` class instantiates a new
650 :class:`_orm.registry` to be used with the base, assuming one was not
651 provided explicitly. The :class:`_orm.DeclarativeBase` class supports
652 class-level attributes which act as parameters for the construction of this
653 registry; such as to indicate a specific :class:`_schema.MetaData`
654 collection as well as a specific value for
655 :paramref:`_orm.registry.type_annotation_map`::
656
657 from typing_extensions import Annotated
658
659 from sqlalchemy import BigInteger
660 from sqlalchemy import MetaData
661 from sqlalchemy import String
662 from sqlalchemy.orm import DeclarativeBase
663
664 bigint = Annotated[int, "bigint"]
665 my_metadata = MetaData()
666
667 class Base(DeclarativeBase):
668 metadata = my_metadata
669 type_annotation_map = {
670 str: String().with_variant(String(255), "mysql", "mariadb"),
671 bigint: BigInteger()
672 }
673
674 Class-level attributes which may be specified include:
675
676 :param metadata: optional :class:`_schema.MetaData` collection.
677 If a :class:`_orm.registry` is constructed automatically, this
678 :class:`_schema.MetaData` collection will be used to construct it.
679 Otherwise, the local :class:`_schema.MetaData` collection will supercede
680 that used by an existing :class:`_orm.registry` passed using the
681 :paramref:`_orm.DeclarativeBase.registry` parameter.
682 :param type_annotation_map: optional type annotation map that will be
683 passed to the :class:`_orm.registry` as
684 :paramref:`_orm.registry.type_annotation_map`.
685 :param registry: supply a pre-existing :class:`_orm.registry` directly.
686
687 .. versionadded:: 2.0 Added :class:`.DeclarativeBase`, so that declarative
688 base classes may be constructed in such a way that is also recognized
689 by :pep:`484` type checkers. As a result, :class:`.DeclarativeBase`
690 and other subclassing-oriented APIs should be seen as
691 superseding previous "class returned by a function" APIs, namely
692 :func:`_orm.declarative_base` and :meth:`_orm.registry.generate_base`,
693 where the base class returned cannot be recognized by type checkers
694 without using plugins.
695
696 **__init__ behavior**
697
698 In a plain Python class, the base-most ``__init__()`` method in the class
699 hierarchy is ``object.__init__()``, which accepts no arguments. However,
700 when the :class:`_orm.DeclarativeBase` subclass is first declared, the
701 class is given an ``__init__()`` method that links to the
702 :paramref:`_orm.registry.constructor` constructor function, if no
703 ``__init__()`` method is already present; this is the usual declarative
704 constructor that will assign keyword arguments as attributes on the
705 instance, assuming those attributes are established at the class level
706 (i.e. are mapped, or are linked to a descriptor). This constructor is
707 **never accessed by a mapped class without being called explicitly via
708 super()**, as mapped classes are themselves given an ``__init__()`` method
709 directly which calls :paramref:`_orm.registry.constructor`, so in the
710 default case works independently of what the base-most ``__init__()``
711 method does.
712
713 .. versionchanged:: 2.0.1 :class:`_orm.DeclarativeBase` has a default
714 constructor that links to :paramref:`_orm.registry.constructor` by
715 default, so that calls to ``super().__init__()`` can access this
716 constructor. Previously, due to an implementation mistake, this default
717 constructor was missing, and calling ``super().__init__()`` would invoke
718 ``object.__init__()``.
719
720 The :class:`_orm.DeclarativeBase` subclass may also declare an explicit
721 ``__init__()`` method which will replace the use of the
722 :paramref:`_orm.registry.constructor` function at this level::
723
724 class Base(DeclarativeBase):
725 def __init__(self, id=None):
726 self.id = id
727
728 Mapped classes still will not invoke this constructor implicitly; it
729 remains only accessible by calling ``super().__init__()``::
730
731 class MyClass(Base):
732 def __init__(self, id=None, name=None):
733 self.name = name
734 super().__init__(id=id)
735
736 Note that this is a different behavior from what functions like the legacy
737 :func:`_orm.declarative_base` would do; the base created by those functions
738 would always install :paramref:`_orm.registry.constructor` for
739 ``__init__()``.
740
741
742 """
743
744 if typing.TYPE_CHECKING:
745
746 def _sa_inspect_type(self) -> Mapper[Self]: ...
747
748 def _sa_inspect_instance(self) -> InstanceState[Self]: ...
749
750 _sa_registry: ClassVar[_RegistryType]
751
752 registry: ClassVar[_RegistryType]
753 """Refers to the :class:`_orm.registry` in use where new
754 :class:`_orm.Mapper` objects will be associated."""
755
756 metadata: ClassVar[MetaData]
757 """Refers to the :class:`_schema.MetaData` collection that will be used
758 for new :class:`_schema.Table` objects.
759
760 .. seealso::
761
762 :ref:`orm_declarative_metadata`
763
764 """
765
766 __name__: ClassVar[str]
767
768 # this ideally should be Mapper[Self], but mypy as of 1.4.1 does not
769 # like it, and breaks the declared_attr_one test. Pyright/pylance is
770 # ok with it.
771 __mapper__: ClassVar[Mapper[Any]]
772 """The :class:`_orm.Mapper` object to which a particular class is
773 mapped.
774
775 May also be acquired using :func:`_sa.inspect`, e.g.
776 ``inspect(klass)``.
777
778 """
779
780 __table__: ClassVar[FromClause]
781 """The :class:`_sql.FromClause` to which a particular subclass is
782 mapped.
783
784 This is usually an instance of :class:`_schema.Table` but may also
785 refer to other kinds of :class:`_sql.FromClause` such as
786 :class:`_sql.Subquery`, depending on how the class is mapped.
787
788 .. seealso::
789
790 :ref:`orm_declarative_metadata`
791
792 """
793
794 # pyright/pylance do not consider a classmethod a ClassVar so use Any
795 # https://github.com/microsoft/pylance-release/issues/3484
796 __tablename__: Any
797 """String name to assign to the generated
798 :class:`_schema.Table` object, if not specified directly via
799 :attr:`_orm.DeclarativeBase.__table__`.
800
801 .. seealso::
802
803 :ref:`orm_declarative_table`
804
805 """
806
807 __mapper_args__: Any
808 """Dictionary of arguments which will be passed to the
809 :class:`_orm.Mapper` constructor.
810
811 .. seealso::
812
813 :ref:`orm_declarative_mapper_options`
814
815 """
816
817 __table_args__: Any
818 """A dictionary or tuple of arguments that will be passed to the
819 :class:`_schema.Table` constructor. See
820 :ref:`orm_declarative_table_configuration`
821 for background on the specific structure of this collection.
822
823 .. seealso::
824
825 :ref:`orm_declarative_table_configuration`
826
827 """
828
829 def __init__(self, **kw: Any): ...
830
831 def __init_subclass__(cls, **kw: Any) -> None:
832 if DeclarativeBase in cls.__bases__:
833 _check_not_declarative(cls, DeclarativeBase)
834 _setup_declarative_base(cls)
835 else:
836 _as_declarative(cls._sa_registry, cls, cls.__dict__)
837 super().__init_subclass__(**kw)
838
839
840def _check_not_declarative(cls: Type[Any], base: Type[Any]) -> None:
841 cls_dict = cls.__dict__
842 if (
843 "__table__" in cls_dict
844 and not (
845 callable(cls_dict["__table__"])
846 or hasattr(cls_dict["__table__"], "__get__")
847 )
848 ) or isinstance(cls_dict.get("__tablename__", None), str):
849 raise exc.InvalidRequestError(
850 f"Cannot use {base.__name__!r} directly as a declarative base "
851 "class. Create a Base by creating a subclass of it."
852 )
853
854
855class DeclarativeBaseNoMeta(
856 # Inspectable is used only by the mypy plugin
857 inspection.Inspectable[InstanceState[Any]]
858):
859 """Same as :class:`_orm.DeclarativeBase`, but does not use a metaclass
860 to intercept new attributes.
861
862 The :class:`_orm.DeclarativeBaseNoMeta` base may be used when use of
863 custom metaclasses is desirable.
864
865 .. versionadded:: 2.0
866
867
868 """
869
870 _sa_registry: ClassVar[_RegistryType]
871
872 registry: ClassVar[_RegistryType]
873 """Refers to the :class:`_orm.registry` in use where new
874 :class:`_orm.Mapper` objects will be associated."""
875
876 metadata: ClassVar[MetaData]
877 """Refers to the :class:`_schema.MetaData` collection that will be used
878 for new :class:`_schema.Table` objects.
879
880 .. seealso::
881
882 :ref:`orm_declarative_metadata`
883
884 """
885
886 # this ideally should be Mapper[Self], but mypy as of 1.4.1 does not
887 # like it, and breaks the declared_attr_one test. Pyright/pylance is
888 # ok with it.
889 __mapper__: ClassVar[Mapper[Any]]
890 """The :class:`_orm.Mapper` object to which a particular class is
891 mapped.
892
893 May also be acquired using :func:`_sa.inspect`, e.g.
894 ``inspect(klass)``.
895
896 """
897
898 __table__: Optional[FromClause]
899 """The :class:`_sql.FromClause` to which a particular subclass is
900 mapped.
901
902 This is usually an instance of :class:`_schema.Table` but may also
903 refer to other kinds of :class:`_sql.FromClause` such as
904 :class:`_sql.Subquery`, depending on how the class is mapped.
905
906 .. seealso::
907
908 :ref:`orm_declarative_metadata`
909
910 """
911
912 if typing.TYPE_CHECKING:
913
914 def _sa_inspect_type(self) -> Mapper[Self]: ...
915
916 def _sa_inspect_instance(self) -> InstanceState[Self]: ...
917
918 __tablename__: Any
919 """String name to assign to the generated
920 :class:`_schema.Table` object, if not specified directly via
921 :attr:`_orm.DeclarativeBase.__table__`.
922
923 .. seealso::
924
925 :ref:`orm_declarative_table`
926
927 """
928
929 __mapper_args__: Any
930 """Dictionary of arguments which will be passed to the
931 :class:`_orm.Mapper` constructor.
932
933 .. seealso::
934
935 :ref:`orm_declarative_mapper_options`
936
937 """
938
939 __table_args__: Any
940 """A dictionary or tuple of arguments that will be passed to the
941 :class:`_schema.Table` constructor. See
942 :ref:`orm_declarative_table_configuration`
943 for background on the specific structure of this collection.
944
945 .. seealso::
946
947 :ref:`orm_declarative_table_configuration`
948
949 """
950
951 def __init__(self, **kw: Any): ...
952
953 def __init_subclass__(cls, **kw: Any) -> None:
954 if DeclarativeBaseNoMeta in cls.__bases__:
955 _check_not_declarative(cls, DeclarativeBaseNoMeta)
956 _setup_declarative_base(cls)
957 else:
958 _as_declarative(cls._sa_registry, cls, cls.__dict__)
959 super().__init_subclass__(**kw)
960
961
962def add_mapped_attribute(
963 target: Type[_O], key: str, attr: MapperProperty[Any]
964) -> None:
965 """Add a new mapped attribute to an ORM mapped class.
966
967 E.g.::
968
969 add_mapped_attribute(User, "addresses", relationship(Address))
970
971 This may be used for ORM mappings that aren't using a declarative
972 metaclass that intercepts attribute set operations.
973
974 .. versionadded:: 2.0
975
976
977 """
978 _add_attribute(target, key, attr)
979
980
981def declarative_base(
982 *,
983 metadata: Optional[MetaData] = None,
984 mapper: Optional[Callable[..., Mapper[Any]]] = None,
985 cls: Type[Any] = object,
986 name: str = "Base",
987 class_registry: Optional[clsregistry._ClsRegistryType] = None,
988 type_annotation_map: Optional[_TypeAnnotationMapType] = None,
989 constructor: Callable[..., None] = _declarative_constructor,
990 metaclass: Type[Any] = DeclarativeMeta,
991) -> Any:
992 r"""Construct a base class for declarative class definitions.
993
994 The new base class will be given a metaclass that produces
995 appropriate :class:`~sqlalchemy.schema.Table` objects and makes
996 the appropriate :class:`_orm.Mapper` calls based on the
997 information provided declaratively in the class and any subclasses
998 of the class.
999
1000 .. versionchanged:: 2.0 Note that the :func:`_orm.declarative_base`
1001 function is superseded by the new :class:`_orm.DeclarativeBase` class,
1002 which generates a new "base" class using subclassing, rather than
1003 return value of a function. This allows an approach that is compatible
1004 with :pep:`484` typing tools.
1005
1006 The :func:`_orm.declarative_base` function is a shorthand version
1007 of using the :meth:`_orm.registry.generate_base`
1008 method. That is, the following::
1009
1010 from sqlalchemy.orm import declarative_base
1011
1012 Base = declarative_base()
1013
1014 Is equivalent to::
1015
1016 from sqlalchemy.orm import registry
1017
1018 mapper_registry = registry()
1019 Base = mapper_registry.generate_base()
1020
1021 See the docstring for :class:`_orm.registry`
1022 and :meth:`_orm.registry.generate_base`
1023 for more details.
1024
1025 .. versionchanged:: 1.4 The :func:`_orm.declarative_base`
1026 function is now a specialization of the more generic
1027 :class:`_orm.registry` class. The function also moves to the
1028 ``sqlalchemy.orm`` package from the ``declarative.ext`` package.
1029
1030
1031 :param metadata:
1032 An optional :class:`~sqlalchemy.schema.MetaData` instance. All
1033 :class:`~sqlalchemy.schema.Table` objects implicitly declared by
1034 subclasses of the base will share this MetaData. A MetaData instance
1035 will be created if none is provided. The
1036 :class:`~sqlalchemy.schema.MetaData` instance will be available via the
1037 ``metadata`` attribute of the generated declarative base class.
1038
1039 :param mapper:
1040 An optional callable, defaults to :class:`_orm.Mapper`. Will
1041 be used to map subclasses to their Tables.
1042
1043 :param cls:
1044 Defaults to :class:`object`. A type to use as the base for the generated
1045 declarative base class. May be a class or tuple of classes.
1046
1047 :param name:
1048 Defaults to ``Base``. The display name for the generated
1049 class. Customizing this is not required, but can improve clarity in
1050 tracebacks and debugging.
1051
1052 :param constructor:
1053 Specify the implementation for the ``__init__`` function on a mapped
1054 class that has no ``__init__`` of its own. Defaults to an
1055 implementation that assigns \**kwargs for declared
1056 fields and relationships to an instance. If ``None`` is supplied,
1057 no __init__ will be provided and construction will fall back to
1058 cls.__init__ by way of the normal Python semantics.
1059
1060 :param class_registry: optional dictionary that will serve as the
1061 registry of class names-> mapped classes when string names
1062 are used to identify classes inside of :func:`_orm.relationship`
1063 and others. Allows two or more declarative base classes
1064 to share the same registry of class names for simplified
1065 inter-base relationships.
1066
1067 :param type_annotation_map: optional dictionary of Python types to
1068 SQLAlchemy :class:`_types.TypeEngine` classes or instances. This
1069 is used exclusively by the :class:`_orm.MappedColumn` construct
1070 to produce column types based on annotations within the
1071 :class:`_orm.Mapped` type.
1072
1073
1074 .. versionadded:: 2.0
1075
1076 .. seealso::
1077
1078 :ref:`orm_declarative_mapped_column_type_map`
1079
1080 :param metaclass:
1081 Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__
1082 compatible callable to use as the meta type of the generated
1083 declarative base class.
1084
1085 .. seealso::
1086
1087 :class:`_orm.registry`
1088
1089 """
1090
1091 return registry(
1092 metadata=metadata,
1093 class_registry=class_registry,
1094 constructor=constructor,
1095 type_annotation_map=type_annotation_map,
1096 ).generate_base(
1097 mapper=mapper,
1098 cls=cls,
1099 name=name,
1100 metaclass=metaclass,
1101 )
1102
1103
1104class registry:
1105 """Generalized registry for mapping classes.
1106
1107 The :class:`_orm.registry` serves as the basis for maintaining a collection
1108 of mappings, and provides configurational hooks used to map classes.
1109
1110 The three general kinds of mappings supported are Declarative Base,
1111 Declarative Decorator, and Imperative Mapping. All of these mapping
1112 styles may be used interchangeably:
1113
1114 * :meth:`_orm.registry.generate_base` returns a new declarative base
1115 class, and is the underlying implementation of the
1116 :func:`_orm.declarative_base` function.
1117
1118 * :meth:`_orm.registry.mapped` provides a class decorator that will
1119 apply declarative mapping to a class without the use of a declarative
1120 base class.
1121
1122 * :meth:`_orm.registry.map_imperatively` will produce a
1123 :class:`_orm.Mapper` for a class without scanning the class for
1124 declarative class attributes. This method suits the use case historically
1125 provided by the ``sqlalchemy.orm.mapper()`` classical mapping function,
1126 which is removed as of SQLAlchemy 2.0.
1127
1128 .. versionadded:: 1.4
1129
1130 .. seealso::
1131
1132 :ref:`orm_mapping_classes_toplevel` - overview of class mapping
1133 styles.
1134
1135 """
1136
1137 _class_registry: clsregistry._ClsRegistryType
1138 _managers: weakref.WeakKeyDictionary[ClassManager[Any], Literal[True]]
1139 _non_primary_mappers: weakref.WeakKeyDictionary[Mapper[Any], Literal[True]]
1140 metadata: MetaData
1141 constructor: CallableReference[Callable[..., None]]
1142 type_annotation_map: _MutableTypeAnnotationMapType
1143 _dependents: Set[_RegistryType]
1144 _dependencies: Set[_RegistryType]
1145 _new_mappers: bool
1146
1147 def __init__(
1148 self,
1149 *,
1150 metadata: Optional[MetaData] = None,
1151 class_registry: Optional[clsregistry._ClsRegistryType] = None,
1152 type_annotation_map: Optional[_TypeAnnotationMapType] = None,
1153 constructor: Callable[..., None] = _declarative_constructor,
1154 ):
1155 r"""Construct a new :class:`_orm.registry`
1156
1157 :param metadata:
1158 An optional :class:`_schema.MetaData` instance. All
1159 :class:`_schema.Table` objects generated using declarative
1160 table mapping will make use of this :class:`_schema.MetaData`
1161 collection. If this argument is left at its default of ``None``,
1162 a blank :class:`_schema.MetaData` collection is created.
1163
1164 :param constructor:
1165 Specify the implementation for the ``__init__`` function on a mapped
1166 class that has no ``__init__`` of its own. Defaults to an
1167 implementation that assigns \**kwargs for declared
1168 fields and relationships to an instance. If ``None`` is supplied,
1169 no __init__ will be provided and construction will fall back to
1170 cls.__init__ by way of the normal Python semantics.
1171
1172 :param class_registry: optional dictionary that will serve as the
1173 registry of class names-> mapped classes when string names
1174 are used to identify classes inside of :func:`_orm.relationship`
1175 and others. Allows two or more declarative base classes
1176 to share the same registry of class names for simplified
1177 inter-base relationships.
1178
1179 :param type_annotation_map: optional dictionary of Python types to
1180 SQLAlchemy :class:`_types.TypeEngine` classes or instances.
1181 The provided dict will update the default type mapping. This
1182 is used exclusively by the :class:`_orm.MappedColumn` construct
1183 to produce column types based on annotations within the
1184 :class:`_orm.Mapped` type.
1185
1186 .. versionadded:: 2.0
1187
1188 .. seealso::
1189
1190 :ref:`orm_declarative_mapped_column_type_map`
1191
1192
1193 """
1194 lcl_metadata = metadata or MetaData()
1195
1196 if class_registry is None:
1197 class_registry = weakref.WeakValueDictionary()
1198
1199 self._class_registry = class_registry
1200 self._managers = weakref.WeakKeyDictionary()
