You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					459 lines
				
				16 KiB
			
		
		
			
		
	
	
					459 lines
				
				16 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# ext/declarative/extensions.py
							 | 
						||
| 
								 | 
							
								# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
							 | 
						||
| 
								 | 
							
								# <see AUTHORS file>
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This module is part of SQLAlchemy and is released under
							 | 
						||
| 
								 | 
							
								# the MIT License: https://www.opensource.org/licenses/mit-license.php
							 | 
						||
| 
								 | 
							
								"""Public API functions and helpers for declarative."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from ... import inspection
							 | 
						||
| 
								 | 
							
								from ... import util
							 | 
						||
| 
								 | 
							
								from ...orm import exc as orm_exc
							 | 
						||
| 
								 | 
							
								from ...orm import registry
							 | 
						||
| 
								 | 
							
								from ...orm import relationships
							 | 
						||
| 
								 | 
							
								from ...orm.base import _mapper_or_none
							 | 
						||
| 
								 | 
							
								from ...orm.clsregistry import _resolver
							 | 
						||
| 
								 | 
							
								from ...orm.decl_base import _DeferredMapperConfig
							 | 
						||
| 
								 | 
							
								from ...orm.util import polymorphic_union
							 | 
						||
| 
								 | 
							
								from ...schema import Table
							 | 
						||
| 
								 | 
							
								from ...util import OrderedDict
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@util.deprecated(
							 | 
						||
| 
								 | 
							
								    "2.0",
							 | 
						||
| 
								 | 
							
								    "the instrument_declarative function is deprecated "
							 | 
						||
| 
								 | 
							
								    "and will be removed in SQLAlhcemy 2.0.  Please use "
							 | 
						||
| 
								 | 
							
								    ":meth:`_orm.registry.map_declaratively",
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								def instrument_declarative(cls, cls_registry, metadata):
							 | 
						||
| 
								 | 
							
								    """Given a class, configure the class declaratively,
							 | 
						||
| 
								 | 
							
								    using the given registry, which can be any dictionary, and
							 | 
						||
| 
								 | 
							
								    MetaData object.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    registry(metadata=metadata, class_registry=cls_registry).map_declaratively(
							 | 
						||
| 
								 | 
							
								        cls
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ConcreteBase(object):
							 | 
						||
| 
								 | 
							
								    """A helper class for 'concrete' declarative mappings.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :class:`.ConcreteBase` will use the :func:`.polymorphic_union`
							 | 
						||
| 
								 | 
							
								    function automatically, against all tables mapped as a subclass
							 | 
						||
| 
								 | 
							
								    to this class.   The function is called via the
							 | 
						||
| 
								 | 
							
								    ``__declare_last__()`` function, which is essentially
							 | 
						||
| 
								 | 
							
								    a hook for the :meth:`.after_configured` event.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :class:`.ConcreteBase` produces a mapped
							 | 
						||
| 
								 | 
							
								    table for the class itself.  Compare to :class:`.AbstractConcreteBase`,
							 | 
						||
| 
								 | 
							
								    which does not.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Example::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        from sqlalchemy.ext.declarative import ConcreteBase
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class Employee(ConcreteBase, Base):
							 | 
						||
| 
								 | 
							
								            __tablename__ = 'employee'
							 | 
						||
| 
								 | 
							
								            employee_id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								            name = Column(String(50))
							 | 
						||
| 
								 | 
							
								            __mapper_args__ = {
							 | 
						||
| 
								 | 
							
								                            'polymorphic_identity':'employee',
							 | 
						||
| 
								 | 
							
								                            'concrete':True}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class Manager(Employee):
							 | 
						||
| 
								 | 
							
								            __tablename__ = 'manager'
							 | 
						||
| 
								 | 
							
								            employee_id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								            name = Column(String(50))
							 | 
						||
| 
								 | 
							
								            manager_data = Column(String(40))
							 | 
						||
| 
								 | 
							
								            __mapper_args__ = {
							 | 
						||
| 
								 | 
							
								                            'polymorphic_identity':'manager',
							 | 
						||
| 
								 | 
							
								                            'concrete':True}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The name of the discriminator column used by :func:`.polymorphic_union`
							 | 
						||
| 
								 | 
							
								    defaults to the name ``type``.  To suit the use case of a mapping where an
							 | 
						||
| 
								 | 
							
								    actual column in a mapped table is already named ``type``, the
							 | 
						||
| 
								 | 
							
								    discriminator name can be configured by setting the
							 | 
						||
| 
								 | 
							
								    ``_concrete_discriminator_name`` attribute::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class Employee(ConcreteBase, Base):
							 | 
						||
| 
								 | 
							
								            _concrete_discriminator_name = '_concrete_discriminator'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionadded:: 1.3.19 Added the ``_concrete_discriminator_name``
							 | 
						||
| 
								 | 
							
								       attribute to :class:`_declarative.ConcreteBase` so that the
							 | 
						||
| 
								 | 
							
								       virtual discriminator column name can be customized.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionchanged:: 1.4.2 The ``_concrete_discriminator_name`` attribute
							 | 
						||
| 
								 | 
							
								       need only be placed on the basemost class to take correct effect for
							 | 
						||
| 
								 | 
							
								       all subclasses.   An explicit error message is now raised if the
							 | 
						||
| 
								 | 
							
								       mapped column names conflict with the discriminator name, whereas
							 | 
						||
| 
								 | 
							
								       in the 1.3.x series there would be some warnings and then a non-useful
							 | 
						||
| 
								 | 
							
								       query would be generated.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :class:`.AbstractConcreteBase`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`concrete_inheritance`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _create_polymorphic_union(cls, mappers, discriminator_name):
							 | 
						||
| 
								 | 
							
								        return polymorphic_union(
							 | 
						||
| 
								 | 
							
								            OrderedDict(
							 | 
						||
| 
								 | 
							
								                (mp.polymorphic_identity, mp.local_table) for mp in mappers
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            discriminator_name,
							 | 
						||
| 
								 | 
							
								            "pjoin",
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def __declare_first__(cls):
							 | 
						||
| 
								 | 
							
								        m = cls.__mapper__
							 | 
						||
| 
								 | 
							
								        if m.with_polymorphic:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        discriminator_name = (
							 | 
						||
| 
								 | 
							
								            getattr(cls, "_concrete_discriminator_name", None) or "type"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        mappers = list(m.self_and_descendants)
							 | 
						||
| 
								 | 
							
								        pjoin = cls._create_polymorphic_union(mappers, discriminator_name)
							 | 
						||
| 
								 | 
							
								        m._set_with_polymorphic(("*", pjoin))
							 | 
						||
| 
								 | 
							
								        m._set_polymorphic_on(pjoin.c[discriminator_name])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class AbstractConcreteBase(ConcreteBase):
							 | 
						||
| 
								 | 
							
								    """A helper class for 'concrete' declarative mappings.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :class:`.AbstractConcreteBase` will use the :func:`.polymorphic_union`
							 | 
						||
| 
								 | 
							
								    function automatically, against all tables mapped as a subclass
							 | 
						||
| 
								 | 
							
								    to this class.   The function is called via the
							 | 
						||
| 
								 | 
							
								    ``__declare_last__()`` function, which is essentially
							 | 
						||
| 
								 | 
							
								    a hook for the :meth:`.after_configured` event.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :class:`.AbstractConcreteBase` does produce a mapped class
							 | 
						||
| 
								 | 
							
								    for the base class, however it is not persisted to any table; it
							 | 
						||
| 
								 | 
							
								    is instead mapped directly to the "polymorphic" selectable directly
							 | 
						||
| 
								 | 
							
								    and is only used for selecting.  Compare to :class:`.ConcreteBase`,
							 | 
						||
| 
								 | 
							
								    which does create a persisted table for the base class.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. note::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The :class:`.AbstractConcreteBase` class does not intend to set up  the
							 | 
						||
| 
								 | 
							
								        mapping for the base class until all the subclasses have been defined,
							 | 
						||
| 
								 | 
							
								        as it needs to create a mapping against a selectable that will include
							 | 
						||
| 
								 | 
							
								        all subclass tables.  In order to achieve this, it waits for the
							 | 
						||
| 
								 | 
							
								        **mapper configuration event** to occur, at which point it scans
							 | 
						||
| 
								 | 
							
								        through all the configured subclasses and sets up a mapping that will
							 | 
						||
| 
								 | 
							
								        query against all subclasses at once.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        While this event is normally invoked automatically, in the case of
							 | 
						||
| 
								 | 
							
								        :class:`.AbstractConcreteBase`, it may be necessary to invoke it
							 | 
						||
| 
								 | 
							
								        explicitly after **all** subclass mappings are defined, if the first
							 | 
						||
| 
								 | 
							
								        operation is to be a query against this base class.  To do so, invoke
							 | 
						||
| 
								 | 
							
								        :func:`.configure_mappers` once all the desired classes have been
							 | 
						||
| 
								 | 
							
								        configured::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            from sqlalchemy.orm import configure_mappers
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            configure_mappers()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            :func:`_orm.configure_mappers`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Example::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        from sqlalchemy.ext.declarative import AbstractConcreteBase
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class Employee(AbstractConcreteBase, Base):
							 | 
						||
| 
								 | 
							
								            pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class Manager(Employee):
							 | 
						||
| 
								 | 
							
								            __tablename__ = 'manager'
							 | 
						||
| 
								 | 
							
								            employee_id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								            name = Column(String(50))
							 | 
						||
| 
								 | 
							
								            manager_data = Column(String(40))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            __mapper_args__ = {
							 | 
						||
| 
								 | 
							
								                'polymorphic_identity':'manager',
							 | 
						||
| 
								 | 
							
								                'concrete':True}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        configure_mappers()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The abstract base class is handled by declarative in a special way;
							 | 
						||
| 
								 | 
							
								    at class configuration time, it behaves like a declarative mixin
							 | 
						||
| 
								 | 
							
								    or an ``__abstract__`` base class.   Once classes are configured
							 | 
						||
| 
								 | 
							
								    and mappings are produced, it then gets mapped itself, but
							 | 
						||
| 
								 | 
							
								    after all of its descendants.  This is a very unique system of mapping
							 | 
						||
| 
								 | 
							
								    not found in any other SQLAlchemy system.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Using this approach, we can specify columns and properties
							 | 
						||
| 
								 | 
							
								    that will take place on mapped subclasses, in the way that
							 | 
						||
| 
								 | 
							
								    we normally do as in :ref:`declarative_mixins`::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class Company(Base):
							 | 
						||
| 
								 | 
							
								            __tablename__ = 'company'
							 | 
						||
| 
								 | 
							
								            id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class Employee(AbstractConcreteBase, Base):
							 | 
						||
| 
								 | 
							
								            employee_id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            @declared_attr
							 | 
						||
| 
								 | 
							
								            def company_id(cls):
							 | 
						||
| 
								 | 
							
								                return Column(ForeignKey('company.id'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            @declared_attr
							 | 
						||
| 
								 | 
							
								            def company(cls):
							 | 
						||
| 
								 | 
							
								                return relationship("Company")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class Manager(Employee):
							 | 
						||
| 
								 | 
							
								            __tablename__ = 'manager'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            name = Column(String(50))
							 | 
						||
| 
								 | 
							
								            manager_data = Column(String(40))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            __mapper_args__ = {
							 | 
						||
| 
								 | 
							
								                'polymorphic_identity':'manager',
							 | 
						||
| 
								 | 
							
								                'concrete':True}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        configure_mappers()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    When we make use of our mappings however, both ``Manager`` and
							 | 
						||
| 
								 | 
							
								    ``Employee`` will have an independently usable ``.company`` attribute::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(Employee).filter(Employee.company.has(id=5))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionchanged:: 1.0.0 - The mechanics of :class:`.AbstractConcreteBase`
							 | 
						||
| 
								 | 
							
								       have been reworked to support relationships established directly
							 | 
						||
| 
								 | 
							
								       on the abstract base, without any special configurational steps.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :class:`.ConcreteBase`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`concrete_inheritance`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    __no_table__ = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def __declare_first__(cls):
							 | 
						||
| 
								 | 
							
								        cls._sa_decl_prepare_nocascade()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _sa_decl_prepare_nocascade(cls):
							 | 
						||
| 
								 | 
							
								        if getattr(cls, "__mapper__", None):
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        to_map = _DeferredMapperConfig.config_for_cls(cls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # can't rely on 'self_and_descendants' here
							 | 
						||
| 
								 | 
							
								        # since technically an immediate subclass
							 | 
						||
| 
								 | 
							
								        # might not be mapped, but a subclass
							 | 
						||
| 
								 | 
							
								        # may be.
							 | 
						||
| 
								 | 
							
								        mappers = []
							 | 
						||
| 
								 | 
							
								        stack = list(cls.__subclasses__())
							 | 
						||
| 
								 | 
							
								        while stack:
							 | 
						||
| 
								 | 
							
								            klass = stack.pop()
							 | 
						||
| 
								 | 
							
								            stack.extend(klass.__subclasses__())
							 | 
						||
| 
								 | 
							
								            mn = _mapper_or_none(klass)
							 | 
						||
| 
								 | 
							
								            if mn is not None:
							 | 
						||
| 
								 | 
							
								                mappers.append(mn)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        discriminator_name = (
							 | 
						||
| 
								 | 
							
								            getattr(cls, "_concrete_discriminator_name", None) or "type"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        pjoin = cls._create_polymorphic_union(mappers, discriminator_name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # For columns that were declared on the class, these
							 | 
						||
| 
								 | 
							
								        # are normally ignored with the "__no_table__" mapping,
							 | 
						||
| 
								 | 
							
								        # unless they have a different attribute key vs. col name
							 | 
						||
| 
								 | 
							
								        # and are in the properties argument.
							 | 
						||
| 
								 | 
							
								        # In that case, ensure we update the properties entry
							 | 
						||
| 
								 | 
							
								        # to the correct column from the pjoin target table.
							 | 
						||
| 
								 | 
							
								        declared_cols = set(to_map.declared_columns)
							 | 
						||
| 
								 | 
							
								        for k, v in list(to_map.properties.items()):
							 | 
						||
| 
								 | 
							
								            if v in declared_cols:
							 | 
						||
| 
								 | 
							
								                to_map.properties[k] = pjoin.c[v.key]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        to_map.local_table = pjoin
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        m_args = to_map.mapper_args_fn or dict
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def mapper_args():
							 | 
						||
| 
								 | 
							
								            args = m_args()
							 | 
						||
| 
								 | 
							
								            args["polymorphic_on"] = pjoin.c[discriminator_name]
							 | 
						||
| 
								 | 
							
								            return args
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        to_map.mapper_args_fn = mapper_args
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        m = to_map.map()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for scls in cls.__subclasses__():
							 | 
						||
| 
								 | 
							
								            sm = _mapper_or_none(scls)
							 | 
						||
| 
								 | 
							
								            if sm and sm.concrete and cls in scls.__bases__:
							 | 
						||
| 
								 | 
							
								                sm._set_concrete_base(m)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _sa_raise_deferred_config(cls):
							 | 
						||
| 
								 | 
							
								        raise orm_exc.UnmappedClassError(
							 | 
						||
| 
								 | 
							
								            cls,
							 | 
						||
| 
								 | 
							
								            msg="Class %s is a subclass of AbstractConcreteBase and "
							 | 
						||
| 
								 | 
							
								            "has a mapping pending until all subclasses are defined. "
							 | 
						||
| 
								 | 
							
								            "Call the sqlalchemy.orm.configure_mappers() function after "
							 | 
						||
| 
								 | 
							
								            "all subclasses have been defined to "
							 | 
						||
| 
								 | 
							
								            "complete the mapping of this class."
							 | 
						||
| 
								 | 
							
								            % orm_exc._safe_cls_name(cls),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class DeferredReflection(object):
							 | 
						||
| 
								 | 
							
								    """A helper class for construction of mappings based on
							 | 
						||
| 
								 | 
							
								    a deferred reflection step.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Normally, declarative can be used with reflection by
							 | 
						||
| 
								 | 
							
								    setting a :class:`_schema.Table` object using autoload_with=engine
							 | 
						||
| 
								 | 
							
								    as the ``__table__`` attribute on a declarative class.
							 | 
						||
| 
								 | 
							
								    The caveat is that the :class:`_schema.Table` must be fully
							 | 
						||
| 
								 | 
							
								    reflected, or at the very least have a primary key column,
							 | 
						||
| 
								 | 
							
								    at the point at which a normal declarative mapping is
							 | 
						||
| 
								 | 
							
								    constructed, meaning the :class:`_engine.Engine` must be available
							 | 
						||
| 
								 | 
							
								    at class declaration time.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The :class:`.DeferredReflection` mixin moves the construction
							 | 
						||
| 
								 | 
							
								    of mappers to be at a later point, after a specific
							 | 
						||
| 
								 | 
							
								    method is called which first reflects all :class:`_schema.Table`
							 | 
						||
| 
								 | 
							
								    objects created so far.   Classes can define it as such::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        from sqlalchemy.ext.declarative import declarative_base
							 | 
						||
| 
								 | 
							
								        from sqlalchemy.ext.declarative import DeferredReflection
							 | 
						||
| 
								 | 
							
								        Base = declarative_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class MyClass(DeferredReflection, Base):
							 | 
						||
| 
								 | 
							
								            __tablename__ = 'mytable'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Above, ``MyClass`` is not yet mapped.   After a series of
							 | 
						||
| 
								 | 
							
								    classes have been defined in the above fashion, all tables
							 | 
						||
| 
								 | 
							
								    can be reflected and mappings created using
							 | 
						||
| 
								 | 
							
								    :meth:`.prepare`::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        engine = create_engine("someengine://...")
							 | 
						||
| 
								 | 
							
								        DeferredReflection.prepare(engine)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The :class:`.DeferredReflection` mixin can be applied to individual
							 | 
						||
| 
								 | 
							
								    classes, used as the base for the declarative base itself,
							 | 
						||
| 
								 | 
							
								    or used in a custom abstract class.   Using an abstract base
							 | 
						||
| 
								 | 
							
								    allows that only a subset of classes to be prepared for a
							 | 
						||
| 
								 | 
							
								    particular prepare step, which is necessary for applications
							 | 
						||
| 
								 | 
							
								    that use more than one engine.  For example, if an application
							 | 
						||
| 
								 | 
							
								    has two engines, you might use two bases, and prepare each
							 | 
						||
| 
								 | 
							
								    separately, e.g.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class ReflectedOne(DeferredReflection, Base):
							 | 
						||
| 
								 | 
							
								            __abstract__ = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class ReflectedTwo(DeferredReflection, Base):
							 | 
						||
| 
								 | 
							
								            __abstract__ = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class MyClass(ReflectedOne):
							 | 
						||
| 
								 | 
							
								            __tablename__ = 'mytable'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class MyOtherClass(ReflectedOne):
							 | 
						||
| 
								 | 
							
								            __tablename__ = 'myothertable'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class YetAnotherClass(ReflectedTwo):
							 | 
						||
| 
								 | 
							
								            __tablename__ = 'yetanothertable'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # ... etc.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Above, the class hierarchies for ``ReflectedOne`` and
							 | 
						||
| 
								 | 
							
								    ``ReflectedTwo`` can be configured separately::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ReflectedOne.prepare(engine_one)
							 | 
						||
| 
								 | 
							
								        ReflectedTwo.prepare(engine_two)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def prepare(cls, engine):
							 | 
						||
| 
								 | 
							
								        """Reflect all :class:`_schema.Table` objects for all current
							 | 
						||
| 
								 | 
							
								        :class:`.DeferredReflection` subclasses"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        to_map = _DeferredMapperConfig.classes_for_base(cls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with inspection.inspect(engine)._inspection_context() as insp:
							 | 
						||
| 
								 | 
							
								            for thingy in to_map:
							 | 
						||
| 
								 | 
							
								                cls._sa_decl_prepare(thingy.local_table, insp)
							 | 
						||
| 
								 | 
							
								                thingy.map()
							 | 
						||
| 
								 | 
							
								                mapper = thingy.cls.__mapper__
							 | 
						||
| 
								 | 
							
								                metadata = mapper.class_.metadata
							 | 
						||
| 
								 | 
							
								                for rel in mapper._props.values():
							 | 
						||
| 
								 | 
							
								                    if (
							 | 
						||
| 
								 | 
							
								                        isinstance(rel, relationships.RelationshipProperty)
							 | 
						||
| 
								 | 
							
								                        and rel.secondary is not None
							 | 
						||
| 
								 | 
							
								                    ):
							 | 
						||
| 
								 | 
							
								                        if isinstance(rel.secondary, Table):
							 | 
						||
| 
								 | 
							
								                            cls._reflect_table(rel.secondary, insp)
							 | 
						||
| 
								 | 
							
								                        elif isinstance(rel.secondary, str):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                            _, resolve_arg = _resolver(rel.parent.class_, rel)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                            rel.secondary = resolve_arg(rel.secondary)
							 | 
						||
| 
								 | 
							
								                            rel.secondary._resolvers += (
							 | 
						||
| 
								 | 
							
								                                cls._sa_deferred_table_resolver(
							 | 
						||
| 
								 | 
							
								                                    insp, metadata
							 | 
						||
| 
								 | 
							
								                                ),
							 | 
						||
| 
								 | 
							
								                            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                            # controversy!  do we resolve it here? or leave
							 | 
						||
| 
								 | 
							
								                            # it deferred?   I think doing it here is necessary
							 | 
						||
| 
								 | 
							
								                            # so the connection does not leak.
							 | 
						||
| 
								 | 
							
								                            rel.secondary = rel.secondary()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _sa_deferred_table_resolver(cls, inspector, metadata):
							 | 
						||
| 
								 | 
							
								        def _resolve(key):
							 | 
						||
| 
								 | 
							
								            t1 = Table(key, metadata)
							 | 
						||
| 
								 | 
							
								            cls._reflect_table(t1, inspector)
							 | 
						||
| 
								 | 
							
								            return t1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return _resolve
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _sa_decl_prepare(cls, local_table, inspector):
							 | 
						||
| 
								 | 
							
								        # autoload Table, which is already
							 | 
						||
| 
								 | 
							
								        # present in the metadata.  This
							 | 
						||
| 
								 | 
							
								        # will fill in db-loaded columns
							 | 
						||
| 
								 | 
							
								        # into the existing Table object.
							 | 
						||
| 
								 | 
							
								        if local_table is not None:
							 | 
						||
| 
								 | 
							
								            cls._reflect_table(local_table, inspector)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _sa_raise_deferred_config(cls):
							 | 
						||
| 
								 | 
							
								        raise orm_exc.UnmappedClassError(
							 | 
						||
| 
								 | 
							
								            cls,
							 | 
						||
| 
								 | 
							
								            msg="Class %s is a subclass of DeferredReflection.  "
							 | 
						||
| 
								 | 
							
								            "Mappings are not produced until the .prepare() "
							 | 
						||
| 
								 | 
							
								            "method is called on the class hierarchy."
							 | 
						||
| 
								 | 
							
								            % orm_exc._safe_cls_name(cls),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _reflect_table(cls, table, inspector):
							 | 
						||
| 
								 | 
							
								        Table(
							 | 
						||
| 
								 | 
							
								            table.name,
							 | 
						||
| 
								 | 
							
								            table.metadata,
							 | 
						||
| 
								 | 
							
								            extend_existing=True,
							 | 
						||
| 
								 | 
							
								            autoload_replace=False,
							 | 
						||
| 
								 | 
							
								            autoload_with=inspector,
							 | 
						||
| 
								 | 
							
								            schema=table.schema,
							 | 
						||
| 
								 | 
							
								        )
							 |