Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/decl_api.py: 68%
206 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1# ext/declarative/api.py
2# Copyright (C) 2005-2022 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"""Public API functions and helpers for declarative."""
8from __future__ import absolute_import
10import itertools
11import re
12import weakref
14from . import attributes
15from . import clsregistry
16from . import exc as orm_exc
17from . import instrumentation
18from . import interfaces
19from . import mapper as mapperlib
20from .base import _inspect_mapped_class
21from .decl_base import _add_attribute
22from .decl_base import _as_declarative
23from .decl_base import _declarative_constructor
24from .decl_base import _DeferredMapperConfig
25from .decl_base import _del_attribute
26from .decl_base import _mapper
27from .descriptor_props import SynonymProperty as _orm_synonym
28from .. import exc
29from .. import inspection
30from .. import util
31from ..sql.schema import MetaData
32from ..util import hybridmethod
33from ..util import hybridproperty
36def has_inherited_table(cls):
37 """Given a class, return True if any of the classes it inherits from has a
38 mapped table, otherwise return False.
40 This is used in declarative mixins to build attributes that behave
41 differently for the base class vs. a subclass in an inheritance
42 hierarchy.
44 .. seealso::
46 :ref:`decl_mixin_inheritance`
48 """
49 for class_ in cls.__mro__[1:]:
50 if getattr(class_, "__table__", None) is not None:
51 return True
52 return False
55class DeclarativeMeta(type):
56 def __init__(cls, classname, bases, dict_, **kw):
57 # use cls.__dict__, which can be modified by an
58 # __init_subclass__() method (#7900)
59 dict_ = cls.__dict__
61 # early-consume registry from the initial declarative base,
62 # assign privately to not conflict with subclass attributes named
63 # "registry"
64 reg = getattr(cls, "_sa_registry", None)
65 if reg is None:
66 reg = dict_.get("registry", None)
67 if not isinstance(reg, registry):
68 raise exc.InvalidRequestError(
69 "Declarative base class has no 'registry' attribute, "
70 "or registry is not a sqlalchemy.orm.registry() object"
71 )
72 else:
73 cls._sa_registry = reg
75 if not cls.__dict__.get("__abstract__", False):
76 _as_declarative(reg, cls, dict_)
77 type.__init__(cls, classname, bases, dict_)
79 def __setattr__(cls, key, value):
80 _add_attribute(cls, key, value)
82 def __delattr__(cls, key):
83 _del_attribute(cls, key)
86def synonym_for(name, map_column=False):
87 """Decorator that produces an :func:`_orm.synonym`
88 attribute in conjunction with a Python descriptor.
90 The function being decorated is passed to :func:`_orm.synonym` as the
91 :paramref:`.orm.synonym.descriptor` parameter::
93 class MyClass(Base):
94 __tablename__ = 'my_table'
96 id = Column(Integer, primary_key=True)
97 _job_status = Column("job_status", String(50))
99 @synonym_for("job_status")
100 @property
101 def job_status(self):
102 return "Status: %s" % self._job_status
104 The :ref:`hybrid properties <mapper_hybrids>` feature of SQLAlchemy
105 is typically preferred instead of synonyms, which is a more legacy
106 feature.
108 .. seealso::
110 :ref:`synonyms` - Overview of synonyms
112 :func:`_orm.synonym` - the mapper-level function
114 :ref:`mapper_hybrids` - The Hybrid Attribute extension provides an
115 updated approach to augmenting attribute behavior more flexibly than
116 can be achieved with synonyms.
118 """
120 def decorate(fn):
121 return _orm_synonym(name, map_column=map_column, descriptor=fn)
123 return decorate
126class declared_attr(interfaces._MappedAttribute, property):
127 """Mark a class-level method as representing the definition of
128 a mapped property or special declarative member name.
130 :class:`_orm.declared_attr` is typically applied as a decorator to a class
131 level method, turning the attribute into a scalar-like property that can be
132 invoked from the uninstantiated class. The Declarative mapping process
133 looks for these :class:`_orm.declared_attr` callables as it scans classes,
134 and assumes any attribute marked with :class:`_orm.declared_attr` will be a
135 callable that will produce an object specific to the Declarative mapping or
136 table configuration.
138 :class:`_orm.declared_attr` is usually applicable to mixins, to define
139 relationships that are to be applied to different implementors of the
140 class. It is also used to define :class:`_schema.Column` objects that
141 include the :class:`_schema.ForeignKey` construct, as these cannot be
142 easily reused across different mappings. The example below illustrates
143 both::
145 class ProvidesUser(object):
146 "A mixin that adds a 'user' relationship to classes."
148 @declared_attr
149 def user_id(self):
150 return Column(ForeignKey("user_account.id"))
152 @declared_attr
153 def user(self):
154 return relationship("User")
156 :class:`_orm.declared_attr` can also be applied to mapped classes, such as
157 to provide a "polymorphic" scheme for inheritance::
159 class Employee(Base):
160 id = Column(Integer, primary_key=True)
161 type = Column(String(50), nullable=False)
163 @declared_attr
164 def __tablename__(cls):
165 return cls.__name__.lower()
167 @declared_attr
168 def __mapper_args__(cls):
169 if cls.__name__ == 'Employee':
170 return {
171 "polymorphic_on":cls.type,
172 "polymorphic_identity":"Employee"
173 }
174 else:
175 return {"polymorphic_identity":cls.__name__}
177 To use :class:`_orm.declared_attr` inside of a Python dataclass
178 as discussed at :ref:`orm_declarative_dataclasses_declarative_table`,
179 it may be placed directly inside the field metadata using a lambda::
181 @dataclass
182 class AddressMixin:
183 __sa_dataclass_metadata_key__ = "sa"
185 user_id: int = field(
186 init=False, metadata={"sa": declared_attr(lambda: Column(ForeignKey("user.id")))}
187 )
188 user: User = field(
189 init=False, metadata={"sa": declared_attr(lambda: relationship(User))}
190 )
192 :class:`_orm.declared_attr` also may be omitted from this form using a
193 lambda directly, as in::
195 user: User = field(
196 init=False, metadata={"sa": lambda: relationship(User)}
197 )
199 .. seealso::
201 :ref:`orm_mixins_toplevel` - illustrates how to use Declarative Mixins
202 which is the primary use case for :class:`_orm.declared_attr`
204 :ref:`orm_declarative_dataclasses_mixin` - illustrates special forms
205 for use with Python dataclasses
207 """ # noqa: E501
209 def __init__(self, fget, cascading=False):
210 super(declared_attr, self).__init__(fget)
211 self.__doc__ = fget.__doc__
212 self._cascading = cascading
214 def __get__(desc, self, cls):
215 # the declared_attr needs to make use of a cache that exists
216 # for the span of the declarative scan_attributes() phase.
217 # to achieve this we look at the class manager that's configured.
218 manager = attributes.manager_of_class(cls)
219 if manager is None:
220 if not re.match(r"^__.+__$", desc.fget.__name__):
221 # if there is no manager at all, then this class hasn't been
222 # run through declarative or mapper() at all, emit a warning.
223 util.warn(
224 "Unmanaged access of declarative attribute %s from "
225 "non-mapped class %s" % (desc.fget.__name__, cls.__name__)
226 )
227 return desc.fget(cls)
228 elif manager.is_mapped:
229 # the class is mapped, which means we're outside of the declarative
230 # scan setup, just run the function.
231 return desc.fget(cls)
233 # here, we are inside of the declarative scan. use the registry
234 # that is tracking the values of these attributes.
235 declarative_scan = manager.declarative_scan()
236 assert declarative_scan is not None
237 reg = declarative_scan.declared_attr_reg
239 if desc in reg:
240 return reg[desc]
241 else:
242 reg[desc] = obj = desc.fget(cls)
243 return obj
245 @hybridmethod
246 def _stateful(cls, **kw):
247 return _stateful_declared_attr(**kw)
249 @hybridproperty
250 def cascading(cls):
251 """Mark a :class:`.declared_attr` as cascading.
253 This is a special-use modifier which indicates that a column
254 or MapperProperty-based declared attribute should be configured
255 distinctly per mapped subclass, within a mapped-inheritance scenario.
257 .. warning::
259 The :attr:`.declared_attr.cascading` modifier has several
260 limitations:
262 * The flag **only** applies to the use of :class:`.declared_attr`
263 on declarative mixin classes and ``__abstract__`` classes; it
264 currently has no effect when used on a mapped class directly.
266 * The flag **only** applies to normally-named attributes, e.g.
267 not any special underscore attributes such as ``__tablename__``.
268 On these attributes it has **no** effect.
270 * The flag currently **does not allow further overrides** down
271 the class hierarchy; if a subclass tries to override the
272 attribute, a warning is emitted and the overridden attribute
273 is skipped. This is a limitation that it is hoped will be
274 resolved at some point.
276 Below, both MyClass as well as MySubClass will have a distinct
277 ``id`` Column object established::
279 class HasIdMixin(object):
280 @declared_attr.cascading
281 def id(cls):
282 if has_inherited_table(cls):
283 return Column(
284 ForeignKey('myclass.id'), primary_key=True
285 )
286 else:
287 return Column(Integer, primary_key=True)
289 class MyClass(HasIdMixin, Base):
290 __tablename__ = 'myclass'
291 # ...
293 class MySubClass(MyClass):
294 ""
295 # ...
297 The behavior of the above configuration is that ``MySubClass``
298 will refer to both its own ``id`` column as well as that of
299 ``MyClass`` underneath the attribute named ``some_id``.
301 .. seealso::
303 :ref:`declarative_inheritance`
305 :ref:`mixin_inheritance_columns`
308 """
309 return cls._stateful(cascading=True)
312class _stateful_declared_attr(declared_attr):
313 def __init__(self, **kw):
314 self.kw = kw
316 def _stateful(self, **kw):
317 new_kw = self.kw.copy()
318 new_kw.update(kw)
319 return _stateful_declared_attr(**new_kw)
321 def __call__(self, fn):
322 return declared_attr(fn, **self.kw)
325def declarative_mixin(cls):
326 """Mark a class as providing the feature of "declarative mixin".
328 E.g.::
330 from sqlalchemy.orm import declared_attr
331 from sqlalchemy.orm import declarative_mixin
333 @declarative_mixin
334 class MyMixin:
336 @declared_attr
337 def __tablename__(cls):
338 return cls.__name__.lower()
340 __table_args__ = {'mysql_engine': 'InnoDB'}
341 __mapper_args__= {'always_refresh': True}
343 id = Column(Integer, primary_key=True)
345 class MyModel(MyMixin, Base):
346 name = Column(String(1000))
348 The :func:`_orm.declarative_mixin` decorator currently does not modify
349 the given class in any way; it's current purpose is strictly to assist
350 the :ref:`Mypy plugin <mypy_toplevel>` in being able to identify
351 SQLAlchemy declarative mixin classes when no other context is present.
353 .. versionadded:: 1.4.6
355 .. seealso::
357 :ref:`orm_mixins_toplevel`
359 :ref:`mypy_declarative_mixins` - in the
360 :ref:`Mypy plugin documentation <mypy_toplevel>`
362 """ # noqa: E501
364 return cls
367def declarative_base(
368 bind=None,
369 metadata=None,
370 mapper=None,
371 cls=object,
372 name="Base",
373 constructor=_declarative_constructor,
374 class_registry=None,
375 metaclass=DeclarativeMeta,
376):
377 r"""Construct a base class for declarative class definitions.
379 The new base class will be given a metaclass that produces
380 appropriate :class:`~sqlalchemy.schema.Table` objects and makes
381 the appropriate :func:`~sqlalchemy.orm.mapper` calls based on the
382 information provided declaratively in the class and any subclasses
383 of the class.
385 The :func:`_orm.declarative_base` function is a shorthand version
386 of using the :meth:`_orm.registry.generate_base`
387 method. That is, the following::
389 from sqlalchemy.orm import declarative_base
391 Base = declarative_base()
393 Is equivalent to::
395 from sqlalchemy.orm import registry
397 mapper_registry = registry()
398 Base = mapper_registry.generate_base()
400 See the docstring for :class:`_orm.registry`
401 and :meth:`_orm.registry.generate_base`
402 for more details.
404 .. versionchanged:: 1.4 The :func:`_orm.declarative_base`
405 function is now a specialization of the more generic
406 :class:`_orm.registry` class. The function also moves to the
407 ``sqlalchemy.orm`` package from the ``declarative.ext`` package.
410 :param bind: An optional
411 :class:`~sqlalchemy.engine.Connectable`, will be assigned
412 the ``bind`` attribute on the :class:`~sqlalchemy.schema.MetaData`
413 instance.
415 .. deprecated:: 1.4 The "bind" argument to declarative_base is
416 deprecated and will be removed in SQLAlchemy 2.0.
418 :param metadata:
419 An optional :class:`~sqlalchemy.schema.MetaData` instance. All
420 :class:`~sqlalchemy.schema.Table` objects implicitly declared by
421 subclasses of the base will share this MetaData. A MetaData instance
422 will be created if none is provided. The
423 :class:`~sqlalchemy.schema.MetaData` instance will be available via the
424 ``metadata`` attribute of the generated declarative base class.
426 :param mapper:
427 An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`. Will
428 be used to map subclasses to their Tables.
430 :param cls:
431 Defaults to :class:`object`. A type to use as the base for the generated
432 declarative base class. May be a class or tuple of classes.
434 :param name:
435 Defaults to ``Base``. The display name for the generated
436 class. Customizing this is not required, but can improve clarity in
437 tracebacks and debugging.
439 :param constructor:
440 Specify the implementation for the ``__init__`` function on a mapped
441 class that has no ``__init__`` of its own. Defaults to an
442 implementation that assigns \**kwargs for declared
443 fields and relationships to an instance. If ``None`` is supplied,
444 no __init__ will be provided and construction will fall back to
445 cls.__init__ by way of the normal Python semantics.
447 :param class_registry: optional dictionary that will serve as the
448 registry of class names-> mapped classes when string names
449 are used to identify classes inside of :func:`_orm.relationship`
450 and others. Allows two or more declarative base classes
451 to share the same registry of class names for simplified
452 inter-base relationships.
454 :param metaclass:
455 Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__
456 compatible callable to use as the meta type of the generated
457 declarative base class.
459 .. seealso::
461 :class:`_orm.registry`
463 """
465 if bind is not None:
466 # util.deprecated_params does not work
467 util.warn_deprecated_20(
468 "The ``bind`` argument to declarative_base is "
469 "deprecated and will be removed in SQLAlchemy 2.0.",
470 )
472 return registry(
473 _bind=bind,
474 metadata=metadata,
475 class_registry=class_registry,
476 constructor=constructor,
477 ).generate_base(
478 mapper=mapper,
479 cls=cls,
480 name=name,
481 metaclass=metaclass,
482 )
485class registry(object):
486 """Generalized registry for mapping classes.
488 The :class:`_orm.registry` serves as the basis for maintaining a collection
489 of mappings, and provides configurational hooks used to map classes.
491 The three general kinds of mappings supported are Declarative Base,
492 Declarative Decorator, and Imperative Mapping. All of these mapping
493 styles may be used interchangeably:
495 * :meth:`_orm.registry.generate_base` returns a new declarative base
496 class, and is the underlying implementation of the
497 :func:`_orm.declarative_base` function.
499 * :meth:`_orm.registry.mapped` provides a class decorator that will
500 apply declarative mapping to a class without the use of a declarative
501 base class.
503 * :meth:`_orm.registry.map_imperatively` will produce a
504 :class:`_orm.Mapper` for a class without scanning the class for
505 declarative class attributes. This method suits the use case historically
506 provided by the
507 :func:`_orm.mapper` classical mapping function.
509 .. versionadded:: 1.4
511 .. seealso::
513 :ref:`orm_mapping_classes_toplevel` - overview of class mapping
514 styles.
516 """
518 def __init__(
519 self,
520 metadata=None,
521 class_registry=None,
522 constructor=_declarative_constructor,
523 _bind=None,
524 ):
525 r"""Construct a new :class:`_orm.registry`
527 :param metadata:
528 An optional :class:`_schema.MetaData` instance. All
529 :class:`_schema.Table` objects generated using declarative
530 table mapping will make use of this :class:`_schema.MetaData`
531 collection. If this argument is left at its default of ``None``,
532 a blank :class:`_schema.MetaData` collection is created.
534 :param constructor:
535 Specify the implementation for the ``__init__`` function on a mapped
536 class that has no ``__init__`` of its own. Defaults to an
537 implementation that assigns \**kwargs for declared
538 fields and relationships to an instance. If ``None`` is supplied,
539 no __init__ will be provided and construction will fall back to
540 cls.__init__ by way of the normal Python semantics.
542 :param class_registry: optional dictionary that will serve as the
543 registry of class names-> mapped classes when string names
544 are used to identify classes inside of :func:`_orm.relationship`
545 and others. Allows two or more declarative base classes
546 to share the same registry of class names for simplified
547 inter-base relationships.
549 """
550 lcl_metadata = metadata or MetaData()
551 if _bind:
552 lcl_metadata.bind = _bind
554 if class_registry is None:
555 class_registry = weakref.WeakValueDictionary()
557 self._class_registry = class_registry
558 self._managers = weakref.WeakKeyDictionary()
559 self._non_primary_mappers = weakref.WeakKeyDictionary()
560 self.metadata = lcl_metadata
561 self.constructor = constructor
563 self._dependents = set()
564 self._dependencies = set()
566 self._new_mappers = False
568 with mapperlib._CONFIGURE_MUTEX:
569 mapperlib._mapper_registries[self] = True
571 @property
572 def mappers(self):
573 """read only collection of all :class:`_orm.Mapper` objects."""
575 return frozenset(manager.mapper for manager in self._managers).union(
576 self._non_primary_mappers
577 )
579 def _set_depends_on(self, registry):
580 if registry is self:
581 return
582 registry._dependents.add(self)
583 self._dependencies.add(registry)
585 def _flag_new_mapper(self, mapper):
586 mapper._ready_for_configure = True
587 if self._new_mappers:
588 return
590 for reg in self._recurse_with_dependents({self}):
591 reg._new_mappers = True
593 @classmethod
594 def _recurse_with_dependents(cls, registries):
595 todo = registries
596 done = set()
597 while todo:
598 reg = todo.pop()
599 done.add(reg)
601 # if yielding would remove dependents, make sure we have
602 # them before
603 todo.update(reg._dependents.difference(done))
604 yield reg
606 # if yielding would add dependents, make sure we have them
607 # after
608 todo.update(reg._dependents.difference(done))
610 @classmethod
611 def _recurse_with_dependencies(cls, registries):
612 todo = registries
613 done = set()
614 while todo:
615 reg = todo.pop()
616 done.add(reg)
618 # if yielding would remove dependencies, make sure we have
619 # them before
620 todo.update(reg._dependencies.difference(done))
622 yield reg
624 # if yielding would remove dependencies, make sure we have
625 # them before
626 todo.update(reg._dependencies.difference(done))
628 def _mappers_to_configure(self):
629 return itertools.chain(
630 (
631 manager.mapper
632 for manager in list(self._managers)
633 if manager.is_mapped
634 and not manager.mapper.configured
635 and manager.mapper._ready_for_configure
636 ),
637 (
638 npm
639 for npm in list(self._non_primary_mappers)
640 if not npm.configured and npm._ready_for_configure
641 ),
642 )
644 def _add_non_primary_mapper(self, np_mapper):
645 self._non_primary_mappers[np_mapper] = True
647 def _dispose_cls(self, cls):
648 clsregistry.remove_class(cls.__name__, cls, self._class_registry)
650 def _add_manager(self, manager):
651 self._managers[manager] = True
652 if manager.registry is not None and manager.is_mapped:
653 raise exc.ArgumentError(
654 "Class '%s' already has a primary mapper defined. "
655 % manager.class_
656 )
657 manager.registry = self
659 def configure(self, cascade=False):
660 """Configure all as-yet unconfigured mappers in this
661 :class:`_orm.registry`.
663 The configure step is used to reconcile and initialize the
664 :func:`_orm.relationship` linkages between mapped classes, as well as
665 to invoke configuration events such as the
666 :meth:`_orm.MapperEvents.before_configured` and
667 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM
668 extensions or user-defined extension hooks.
670 If one or more mappers in this registry contain
671 :func:`_orm.relationship` constructs that refer to mapped classes in
672 other registries, this registry is said to be *dependent* on those
673 registries. In order to configure those dependent registries
674 automatically, the :paramref:`_orm.registry.configure.cascade` flag
675 should be set to ``True``. Otherwise, if they are not configured, an
676 exception will be raised. The rationale behind this behavior is to
677 allow an application to programmatically invoke configuration of
678 registries while controlling whether or not the process implicitly
679 reaches other registries.
681 As an alternative to invoking :meth:`_orm.registry.configure`, the ORM
682 function :func:`_orm.configure_mappers` function may be used to ensure
683 configuration is complete for all :class:`_orm.registry` objects in
684 memory. This is generally simpler to use and also predates the usage of
685 :class:`_orm.registry` objects overall. However, this function will
686 impact all mappings throughout the running Python process and may be
687 more memory/time consuming for an application that has many registries
688 in use for different purposes that may not be needed immediately.
690 .. seealso::
692 :func:`_orm.configure_mappers`
695 .. versionadded:: 1.4.0b2
697 """
698 mapperlib._configure_registries({self}, cascade=cascade)
700 def dispose(self, cascade=False):
701 """Dispose of all mappers in this :class:`_orm.registry`.
703 After invocation, all the classes that were mapped within this registry
704 will no longer have class instrumentation associated with them. This
705 method is the per-:class:`_orm.registry` analogue to the
706 application-wide :func:`_orm.clear_mappers` function.
708 If this registry contains mappers that are dependencies of other
709 registries, typically via :func:`_orm.relationship` links, then those
710 registries must be disposed as well. When such registries exist in
711 relation to this one, their :meth:`_orm.registry.dispose` method will
712 also be called, if the :paramref:`_orm.registry.dispose.cascade` flag
713 is set to ``True``; otherwise, an error is raised if those registries
714 were not already disposed.
716 .. versionadded:: 1.4.0b2
718 .. seealso::
720 :func:`_orm.clear_mappers`
722 """
724 mapperlib._dispose_registries({self}, cascade=cascade)
726 def _dispose_manager_and_mapper(self, manager):
727 if "mapper" in manager.__dict__:
728 mapper = manager.mapper
730 mapper._set_dispose_flags()
732 class_ = manager.class_
733 self._dispose_cls(class_)
734 instrumentation._instrumentation_factory.unregister(class_)
736 def generate_base(
737 self,
738 mapper=None,
739 cls=object,
740 name="Base",
741 metaclass=DeclarativeMeta,
742 ):
743 """Generate a declarative base class.
745 Classes that inherit from the returned class object will be
746 automatically mapped using declarative mapping.
748 E.g.::
750 from sqlalchemy.orm import registry
752 mapper_registry = registry()
754 Base = mapper_registry.generate_base()
756 class MyClass(Base):
757 __tablename__ = "my_table"
758 id = Column(Integer, primary_key=True)
760 The above dynamically generated class is equivalent to the
761 non-dynamic example below::
763 from sqlalchemy.orm import registry
764 from sqlalchemy.orm.decl_api import DeclarativeMeta
766 mapper_registry = registry()
768 class Base(metaclass=DeclarativeMeta):
769 __abstract__ = True
770 registry = mapper_registry
771 metadata = mapper_registry.metadata
773 __init__ = mapper_registry.constructor
775 The :meth:`_orm.registry.generate_base` method provides the
776 implementation for the :func:`_orm.declarative_base` function, which
777 creates the :class:`_orm.registry` and base class all at once.
779 See the section :ref:`orm_declarative_mapping` for background and
780 examples.
782 :param mapper:
783 An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`.
784 This function is used to generate new :class:`_orm.Mapper` objects.
786 :param cls:
787 Defaults to :class:`object`. A type to use as the base for the
788 generated declarative base class. May be a class or tuple of classes.
790 :param name:
791 Defaults to ``Base``. The display name for the generated
792 class. Customizing this is not required, but can improve clarity in
793 tracebacks and debugging.
795 :param metaclass:
796 Defaults to :class:`.DeclarativeMeta`. A metaclass or __metaclass__
797 compatible callable to use as the meta type of the generated
798 declarative base class.
800 .. seealso::
802 :ref:`orm_declarative_mapping`
804 :func:`_orm.declarative_base`
806 """
807 metadata = self.metadata
809 bases = not isinstance(cls, tuple) and (cls,) or cls
811 class_dict = dict(registry=self, metadata=metadata)
812 if isinstance(cls, type):
813 class_dict["__doc__"] = cls.__doc__
815 if self.constructor:
816 class_dict["__init__"] = self.constructor
818 class_dict["__abstract__"] = True
819 if mapper:
820 class_dict["__mapper_cls__"] = mapper
822 if hasattr(cls, "__class_getitem__"):
824 def __class_getitem__(cls, key):
825 # allow generic classes in py3.9+
826 return cls
828 class_dict["__class_getitem__"] = __class_getitem__
830 return metaclass(name, bases, class_dict)
832 def mapped(self, cls):
833 """Class decorator that will apply the Declarative mapping process
834 to a given class.
836 E.g.::
838 from sqlalchemy.orm import registry
840 mapper_registry = registry()
842 @mapper_registry.mapped
843 class Foo:
844 __tablename__ = 'some_table'
846 id = Column(Integer, primary_key=True)
847 name = Column(String)
849 See the section :ref:`orm_declarative_mapping` for complete
850 details and examples.
852 :param cls: class to be mapped.
854 :return: the class that was passed.
856 .. seealso::
858 :ref:`orm_declarative_mapping`
860 :meth:`_orm.registry.generate_base` - generates a base class
861 that will apply Declarative mapping to subclasses automatically
862 using a Python metaclass.
864 """
865 _as_declarative(self, cls, cls.__dict__)
866 return cls
868 def as_declarative_base(self, **kw):
869 """
870 Class decorator which will invoke
871 :meth:`_orm.registry.generate_base`
872 for a given base class.
874 E.g.::
876 from sqlalchemy.orm import registry
878 mapper_registry = registry()
880 @mapper_registry.as_declarative_base()
881 class Base(object):
882 @declared_attr
883 def __tablename__(cls):
884 return cls.__name__.lower()
885 id = Column(Integer, primary_key=True)
887 class MyMappedClass(Base):
888 # ...
890 All keyword arguments passed to
891 :meth:`_orm.registry.as_declarative_base` are passed
892 along to :meth:`_orm.registry.generate_base`.
894 """
896 def decorate(cls):
897 kw["cls"] = cls
898 kw["name"] = cls.__name__
899 return self.generate_base(**kw)
901 return decorate
903 def map_declaratively(self, cls):
904 """Map a class declaratively.
906 In this form of mapping, the class is scanned for mapping information,
907 including for columns to be associated with a table, and/or an
908 actual table object.
910 Returns the :class:`_orm.Mapper` object.
912 E.g.::
914 from sqlalchemy.orm import registry
916 mapper_registry = registry()
918 class Foo:
919 __tablename__ = 'some_table'
921 id = Column(Integer, primary_key=True)
922 name = Column(String)
924 mapper = mapper_registry.map_declaratively(Foo)
926 This function is more conveniently invoked indirectly via either the
927 :meth:`_orm.registry.mapped` class decorator or by subclassing a
928 declarative metaclass generated from
929 :meth:`_orm.registry.generate_base`.
931 See the section :ref:`orm_declarative_mapping` for complete
932 details and examples.
934 :param cls: class to be mapped.
936 :return: a :class:`_orm.Mapper` object.
938 .. seealso::
940 :ref:`orm_declarative_mapping`
942 :meth:`_orm.registry.mapped` - more common decorator interface
943 to this function.
945 :meth:`_orm.registry.map_imperatively`
947 """
948 return _as_declarative(self, cls, cls.__dict__)
950 def map_imperatively(self, class_, local_table=None, **kw):
951 r"""Map a class imperatively.
953 In this form of mapping, the class is not scanned for any mapping
954 information. Instead, all mapping constructs are passed as
955 arguments.
957 This method is intended to be fully equivalent to the classic
958 SQLAlchemy :func:`_orm.mapper` function, except that it's in terms of
959 a particular registry.
961 E.g.::
963 from sqlalchemy.orm import registry
965 mapper_registry = registry()
967 my_table = Table(
968 "my_table",
969 mapper_registry.metadata,
970 Column('id', Integer, primary_key=True)
971 )
973 class MyClass:
974 pass
976 mapper_registry.map_imperatively(MyClass, my_table)
978 See the section :ref:`orm_imperative_mapping` for complete background
979 and usage examples.
981 :param class\_: The class to be mapped. Corresponds to the
982 :paramref:`_orm.mapper.class_` parameter.
984 :param local_table: the :class:`_schema.Table` or other
985 :class:`_sql.FromClause` object that is the subject of the mapping.
986 Corresponds to the
987 :paramref:`_orm.mapper.local_table` parameter.
989 :param \**kw: all other keyword arguments are passed to the
990 :func:`_orm.mapper` function directly.
992 .. seealso::
994 :ref:`orm_imperative_mapping`
996 :ref:`orm_declarative_mapping`
998 """
999 return _mapper(self, class_, local_table, kw)
1002mapperlib._legacy_registry = registry()
1005@util.deprecated_params(
1006 bind=(
1007 "2.0",
1008 "The ``bind`` argument to as_declarative is "
1009 "deprecated and will be removed in SQLAlchemy 2.0.",
1010 )
1011)
1012def as_declarative(**kw):
1013 """
1014 Class decorator which will adapt a given class into a
1015 :func:`_orm.declarative_base`.
1017 This function makes use of the :meth:`_orm.registry.as_declarative_base`
1018 method, by first creating a :class:`_orm.registry` automatically
1019 and then invoking the decorator.
1021 E.g.::
1023 from sqlalchemy.orm import as_declarative
1025 @as_declarative()
1026 class Base(object):
1027 @declared_attr
1028 def __tablename__(cls):
1029 return cls.__name__.lower()
1030 id = Column(Integer, primary_key=True)
1032 class MyMappedClass(Base):
1033 # ...
1035 .. seealso::
1037 :meth:`_orm.registry.as_declarative_base`
1039 """
1040 bind, metadata, class_registry = (
1041 kw.pop("bind", None),
1042 kw.pop("metadata", None),
1043 kw.pop("class_registry", None),
1044 )
1046 return registry(
1047 _bind=bind, metadata=metadata, class_registry=class_registry
1048 ).as_declarative_base(**kw)
1051@inspection._inspects(DeclarativeMeta)
1052def _inspect_decl_meta(cls):
1053 mp = _inspect_mapped_class(cls)
1054 if mp is None:
1055 if _DeferredMapperConfig.has_cls(cls):
1056 _DeferredMapperConfig.raise_unmapped_for_cls(cls)
1057 raise orm_exc.UnmappedClassError(
1058 cls,
1059 msg="Class %s has a deferred mapping on it. It is not yet "
1060 "usable as a mapped class." % orm_exc._safe_cls_name(cls),
1061 )
1062 return mp