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"""Public API functions and helpers for declarative."""
8from __future__ import absolute_import
9
10import itertools
11import re
12import weakref
13
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
34
35
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.
39
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.
43
44 .. seealso::
45
46 :ref:`decl_mixin_inheritance`
47
48 """
49 for class_ in cls.__mro__[1:]:
50 if getattr(class_, "__table__", None) is not None:
51 return True
52 return False
53
54
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__
60
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
74
75 if not cls.__dict__.get("__abstract__", False):
76 _as_declarative(reg, cls, dict_)
77 type.__init__(cls, classname, bases, dict_)
78
79 def __setattr__(cls, key, value):
80 _add_attribute(cls, key, value)
81
82 def __delattr__(cls, key):
83 _del_attribute(cls, key)
84
85
86def synonym_for(name, map_column=False):
87 """Decorator that produces an :func:`_orm.synonym`
88 attribute in conjunction with a Python descriptor.
89
90 The function being decorated is passed to :func:`_orm.synonym` as the
91 :paramref:`.orm.synonym.descriptor` parameter::
92
93 class MyClass(Base):
94 __tablename__ = 'my_table'
95
96 id = Column(Integer, primary_key=True)
97 _job_status = Column("job_status", String(50))
98
99 @synonym_for("job_status")
100 @property
101 def job_status(self):
102 return "Status: %s" % self._job_status
103
104 The :ref:`hybrid properties <mapper_hybrids>` feature of SQLAlchemy
105 is typically preferred instead of synonyms, which is a more legacy
106 feature.
107
108 .. seealso::
109
110 :ref:`synonyms` - Overview of synonyms
111
112 :func:`_orm.synonym` - the mapper-level function
113
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.
117
118 """
119
120 def decorate(fn):
121 return _orm_synonym(name, map_column=map_column, descriptor=fn)
122
123 return decorate
124
125
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.
129
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.
137
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::
144
145 class ProvidesUser(object):
146 "A mixin that adds a 'user' relationship to classes."
147
148 @declared_attr
149 def user_id(self):
150 return Column(ForeignKey("user_account.id"))
151
152 @declared_attr
153 def user(self):
154 return relationship("User")
155
156 :class:`_orm.declared_attr` can also be applied to mapped classes, such as
157 to provide a "polymorphic" scheme for inheritance::
158
159 class Employee(Base):
160 id = Column(Integer, primary_key=True)
161 type = Column(String(50), nullable=False)
162
163 @declared_attr
164 def __tablename__(cls):
165 return cls.__name__.lower()
166
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__}
176
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::
180
181 @dataclass
182 class AddressMixin:
183 __sa_dataclass_metadata_key__ = "sa"
184
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 )
191
192 :class:`_orm.declared_attr` also may be omitted from this form using a
193 lambda directly, as in::
194
195 user: User = field(
196 init=False, metadata={"sa": lambda: relationship(User)}
197 )
198
199 .. seealso::
200
201 :ref:`orm_mixins_toplevel` - illustrates how to use Declarative Mixins
202 which is the primary use case for :class:`_orm.declared_attr`
203
204 :ref:`orm_declarative_dataclasses_mixin` - illustrates special forms
205 for use with Python dataclasses
206
207 """ # noqa: E501
208
209 def __init__(self, fget, cascading=False):
210 super(declared_attr, self).__init__(fget)
211 self.__doc__ = fget.__doc__
212 self._cascading = cascading
213
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)
232
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
238
239 if desc in reg:
240 return reg[desc]
241 else:
242 reg[desc] = obj = desc.fget(cls)
243 return obj
244
245 @hybridmethod
246 def _stateful(cls, **kw):
247 return _stateful_declared_attr(**kw)
248
249 @hybridproperty
250 def cascading(cls):
251 """Mark a :class:`.declared_attr` as cascading.
252
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.
256
257 .. warning::
258
259 The :attr:`.declared_attr.cascading` modifier has several
260 limitations:
261
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.
265
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.
269
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.
275
276 Below, both MyClass as well as MySubClass will have a distinct
277 ``id`` Column object established::
278
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)
288
289 class MyClass(HasIdMixin, Base):
290 __tablename__ = 'myclass'
291 # ...
292
293 class MySubClass(MyClass):
294 ""
295 # ...
296
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``.
300
301 .. seealso::
302
303 :ref:`declarative_inheritance`
304
305 :ref:`mixin_inheritance_columns`
306
307
308 """
309 return cls._stateful(cascading=True)
310
311
312class _stateful_declared_attr(declared_attr):
313 def __init__(self, **kw):
314 self.kw = kw
315
316 def _stateful(self, **kw):
317 new_kw = self.kw.copy()
318 new_kw.update(kw)
319 return _stateful_declared_attr(**new_kw)
320
321 def __call__(self, fn):
322 return declared_attr(fn, **self.kw)
323
324
325def declarative_mixin(cls):
326 """Mark a class as providing the feature of "declarative mixin".
327
328 E.g.::
329
330 from sqlalchemy.orm import declared_attr
331 from sqlalchemy.orm import declarative_mixin
332
333 @declarative_mixin
334 class MyMixin:
335
336 @declared_attr
337 def __tablename__(cls):
338 return cls.__name__.lower()
339
340 __table_args__ = {'mysql_engine': 'InnoDB'}
341 __mapper_args__= {'always_refresh': True}
342
343 id = Column(Integer, primary_key=True)
344
345 class MyModel(MyMixin, Base):
346 name = Column(String(1000))
347
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.
352
353 .. versionadded:: 1.4.6
354
355 .. seealso::
356
357 :ref:`orm_mixins_toplevel`
358
359 :ref:`mypy_declarative_mixins` - in the
360 :ref:`Mypy plugin documentation <mypy_toplevel>`
361
362 """ # noqa: E501
363
364 return cls
365
366
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.
378
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.
384
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::
388
389 from sqlalchemy.orm import declarative_base
390
391 Base = declarative_base()
392
393 Is equivalent to::
394
395 from sqlalchemy.orm import registry
396
397 mapper_registry = registry()
398 Base = mapper_registry.generate_base()
399
400 See the docstring for :class:`_orm.registry`
401 and :meth:`_orm.registry.generate_base`
402 for more details.
403
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.
408
409
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.
414
415 .. deprecated:: 1.4 The "bind" argument to declarative_base is
416 deprecated and will be removed in SQLAlchemy 2.0.
417
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.
425
426 :param mapper:
427 An optional callable, defaults to :func:`~sqlalchemy.orm.mapper`. Will
428 be used to map subclasses to their Tables.
429
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.
433
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.
438
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.
446
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.
453
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.
458
459 .. seealso::
460
461 :class:`_orm.registry`
462
463 """
464
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 )
471
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 )
483
484
485class registry(object):
486 """Generalized registry for mapping classes.
487
488 The :class:`_orm.registry` serves as the basis for maintaining a collection
489 of mappings, and provides configurational hooks used to map classes.
490
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:
494
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.
498
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.
502
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.
508
509 .. versionadded:: 1.4
510
511 .. seealso::
512
513 :ref:`orm_mapping_classes_toplevel` - overview of class mapping
514 styles.
515
516 """
517
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`
526
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.
533
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.
541
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.
548
549 """
550 lcl_metadata = metadata or MetaData()
551 if _bind:
552 lcl_metadata.bind = _bind
553
554 if class_registry is None:
555 class_registry = weakref.WeakValueDictionary()
556
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
562
563 self._dependents = set()
564 self._dependencies = set()
565
566 self._new_mappers = False
567
568 with mapperlib._CONFIGURE_MUTEX:
569 mapperlib._mapper_registries[self] = True
570
571 @property
572 def mappers(self):
573 """read only collection of all :class:`_orm.Mapper` objects."""
574
575 return frozenset(manager.mapper for manager in self._managers).union(
576 self._non_primary_mappers
577 )
578
579 def _set_depends_on(self, registry):
580 if registry is self:
581 return
582 registry._dependents.add(self)
583 self._dependencies.add(registry)
584
585 def _flag_new_mapper(self, mapper):
586 mapper._ready_for_configure = True
587 if self._new_mappers:
588 return
589
590 for reg in self._recurse_with_dependents({self}):
591 reg._new_mappers = True
592
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)
600
601 # if yielding would remove dependents, make sure we have
602 # them before
603 todo.update(reg._dependents.difference(done))
604 yield reg
605
606 # if yielding would add dependents, make sure we have them
607 # after
608 todo.update(reg._dependents.difference(done))
609
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)
617
618 # if yielding would remove dependencies, make sure we have
619 # them before
620 todo.update(reg._dependencies.difference(done))
621
622 yield reg
623
624 # if yielding would remove dependencies, make sure we have
625 # them before
626 todo.update(reg._dependencies.difference(done))
627
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 )
643
644 def _add_non_primary_mapper(self, np_mapper):
645 self._non_primary_mappers[np_mapper] = True
646
647 def _dispose_cls(self, cls):
648 clsregistry.remove_class(cls.__name__, cls, self._class_registry)
649
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
658
659 def configure(self, cascade=False):
660 """Configure all as-yet unconfigured mappers in this
661 :class:`_orm.registry`.
662
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.
669
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.
680
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.
689
690 .. seealso::
691
692 :func:`_orm.configure_mappers`
693
694
695 .. versionadded:: 1.4.0b2
696
697 """
698 mapperlib._configure_registries({self}, cascade=cascade)
699
700 def dispose(self, cascade=False):
701 """Dispose of all mappers in this :class:`_orm.registry`.
702
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.
707
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.
715
716 .. versionadded:: 1.4.0b2
717
718 .. seealso::
719
720 :func:`_orm.clear_mappers`
721
722 """
723
724 mapperlib._dispose_registries({self}, cascade=cascade)
725
726 def _dispose_manager_and_mapper(self, manager):
727 if "mapper" in manager.__dict__:
728 mapper = manager.mapper
729
730 mapper._set_dispose_flags()
731
732 class_ = manager.class_
733 self._dispose_cls(class_)
734 instrumentation._instrumentation_factory.unregister(class_)
735
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.
744
745 Classes that inherit from the returned class object will be
746 automatically mapped using declarative mapping.
747
748 E.g.::
749
750 from sqlalchemy.orm import registry
751
752 mapper_registry = registry()
753
754 Base = mapper_registry.generate_base()
755
756 class MyClass(Base):
757 __tablename__ = "my_table"
758 id = Column(Integer, primary_key=True)
759
760 The above dynamically generated class is equivalent to the
761 non-dynamic example below::
762
763 from sqlalchemy.orm import registry
764 from sqlalchemy.orm.decl_api import DeclarativeMeta
765
766 mapper_registry = registry()
767
768 class Base(metaclass=DeclarativeMeta):
769 __abstract__ = True
770 registry = mapper_registry
771 metadata = mapper_registry.metadata
772
773 __init__ = mapper_registry.constructor
774
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.
778
779 See the section :ref:`orm_declarative_mapping` for background and
780 examples.
781
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.
785
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.
789
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.
794
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.
799
800 .. seealso::
801
802 :ref:`orm_declarative_mapping`
803
804 :func:`_orm.declarative_base`
805
806 """
807 metadata = self.metadata
808
809 bases = not isinstance(cls, tuple) and (cls,) or cls
810
811 class_dict = dict(registry=self, metadata=metadata)
812 if isinstance(cls, type):
813 class_dict["__doc__"] = cls.__doc__
814
815 if self.constructor:
816 class_dict["__init__"] = self.constructor
817
818 class_dict["__abstract__"] = True
819 if mapper:
820 class_dict["__mapper_cls__"] = mapper
821
822 if hasattr(cls, "__class_getitem__"):
823
824 def __class_getitem__(cls, key):
825 # allow generic classes in py3.9+
826 return cls
827
828 class_dict["__class_getitem__"] = __class_getitem__
829
830 return metaclass(name, bases, class_dict)
831
832 def mapped(self, cls):
833 """Class decorator that will apply the Declarative mapping process
834 to a given class.
835
836 E.g.::
837
838 from sqlalchemy.orm import registry
839
840 mapper_registry = registry()
841
842 @mapper_registry.mapped
843 class Foo:
844 __tablename__ = 'some_table'
845
846 id = Column(Integer, primary_key=True)
847 name = Column(String)
848
849 See the section :ref:`orm_declarative_mapping` for complete
850 details and examples.
851
852 :param cls: class to be mapped.
853
854 :return: the class that was passed.
855
856 .. seealso::
857
858 :ref:`orm_declarative_mapping`
859
860 :meth:`_orm.registry.generate_base` - generates a base class
861 that will apply Declarative mapping to subclasses automatically
862 using a Python metaclass.
863
864 """
865 _as_declarative(self, cls, cls.__dict__)
866 return cls
867
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.
873
874 E.g.::
875
876 from sqlalchemy.orm import registry
877
878 mapper_registry = registry()
879
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)
886
887 class MyMappedClass(Base):
888 # ...
889
890 All keyword arguments passed to
891 :meth:`_orm.registry.as_declarative_base` are passed
892 along to :meth:`_orm.registry.generate_base`.
893
894 """
895
896 def decorate(cls):
897 kw["cls"] = cls
898 kw["name"] = cls.__name__
899 return self.generate_base(**kw)
900
901 return decorate
902
903 def map_declaratively(self, cls):
904 """Map a class declaratively.
905
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.
909
910 Returns the :class:`_orm.Mapper` object.
911
912 E.g.::
913
914 from sqlalchemy.orm import registry
915
916 mapper_registry = registry()
917
918 class Foo:
919 __tablename__ = 'some_table'
920
921 id = Column(Integer, primary_key=True)
922 name = Column(String)
923
924 mapper = mapper_registry.map_declaratively(Foo)
925
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`.
930
931 See the section :ref:`orm_declarative_mapping` for complete
932 details and examples.
933
934 :param cls: class to be mapped.
935
936 :return: a :class:`_orm.Mapper` object.
937
938 .. seealso::
939
940 :ref:`orm_declarative_mapping`
941
942 :meth:`_orm.registry.mapped` - more common decorator interface
943 to this function.
944
945 :meth:`_orm.registry.map_imperatively`
946
947 """
948 return _as_declarative(self, cls, cls.__dict__)
949
950 def map_imperatively(self, class_, local_table=None, **kw):
951 r"""Map a class imperatively.
952
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.
956
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.
960
961 E.g.::
962
963 from sqlalchemy.orm import registry
964
965 mapper_registry = registry()
966
967 my_table = Table(
968 "my_table",
969 mapper_registry.metadata,
970 Column('id', Integer, primary_key=True)
971 )
972
973 class MyClass:
974 pass
975
976 mapper_registry.map_imperatively(MyClass, my_table)
977
978 See the section :ref:`orm_imperative_mapping` for complete background
979 and usage examples.
980
981 :param class\_: The class to be mapped. Corresponds to the
982 :paramref:`_orm.mapper.class_` parameter.
983
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.
988
989 :param \**kw: all other keyword arguments are passed to the
990 :func:`_orm.mapper` function directly.
991
992 .. seealso::
993
994 :ref:`orm_imperative_mapping`
995
996 :ref:`orm_declarative_mapping`
997
998 """
999 return _mapper(self, class_, local_table, kw)
1000
1001
1002mapperlib._legacy_registry = registry()
1003
1004
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`.
1016
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.
1020
1021 E.g.::
1022
1023 from sqlalchemy.orm import as_declarative
1024
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)
1031
1032 class MyMappedClass(Base):
1033 # ...
1034
1035 .. seealso::
1036
1037 :meth:`_orm.registry.as_declarative_base`
1038
1039 """
1040 bind, metadata, class_registry = (
1041 kw.pop("bind", None),
1042 kw.pop("metadata", None),
1043 kw.pop("class_registry", None),
1044 )
1045
1046 return registry(
1047 _bind=bind, metadata=metadata, class_registry=class_registry
1048 ).as_declarative_base(**kw)
1049
1050
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