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.
		
		
		
		
		
			
		
			
				
					2009 lines
				
				66 KiB
			
		
		
			
		
	
	
					2009 lines
				
				66 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# 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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from . import util as orm_util
							 | 
						||
| 
								 | 
							
								from .attributes import QueryableAttribute
							 | 
						||
| 
								 | 
							
								from .base import _class_to_mapper
							 | 
						||
| 
								 | 
							
								from .base import _is_aliased_class
							 | 
						||
| 
								 | 
							
								from .base import _is_mapped_class
							 | 
						||
| 
								 | 
							
								from .base import InspectionAttr
							 | 
						||
| 
								 | 
							
								from .interfaces import LoaderOption
							 | 
						||
| 
								 | 
							
								from .interfaces import MapperProperty
							 | 
						||
| 
								 | 
							
								from .interfaces import PropComparator
							 | 
						||
| 
								 | 
							
								from .path_registry import _DEFAULT_TOKEN
							 | 
						||
| 
								 | 
							
								from .path_registry import _WILDCARD_TOKEN
							 | 
						||
| 
								 | 
							
								from .path_registry import PathRegistry
							 | 
						||
| 
								 | 
							
								from .path_registry import TokenRegistry
							 | 
						||
| 
								 | 
							
								from .util import _orm_full_deannotate
							 | 
						||
| 
								 | 
							
								from .. import exc as sa_exc
							 | 
						||
| 
								 | 
							
								from .. import inspect
							 | 
						||
| 
								 | 
							
								from .. import util
							 | 
						||
| 
								 | 
							
								from ..sql import and_
							 | 
						||
| 
								 | 
							
								from ..sql import coercions
							 | 
						||
| 
								 | 
							
								from ..sql import roles
							 | 
						||
| 
								 | 
							
								from ..sql import traversals
							 | 
						||
| 
								 | 
							
								from ..sql import visitors
							 | 
						||
| 
								 | 
							
								from ..sql.base import _generative
							 | 
						||
| 
								 | 
							
								from ..sql.base import Generative
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Load(Generative, LoaderOption):
							 | 
						||
| 
								 | 
							
								    """Represents loader options which modify the state of a
							 | 
						||
| 
								 | 
							
								    :class:`_query.Query` in order to affect how various mapped attributes are
							 | 
						||
| 
								 | 
							
								    loaded.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The :class:`_orm.Load` object is in most cases used implicitly behind the
							 | 
						||
| 
								 | 
							
								    scenes when one makes use of a query option like :func:`_orm.joinedload`,
							 | 
						||
| 
								 | 
							
								    :func:`.defer`, or similar.   However, the :class:`_orm.Load` object
							 | 
						||
| 
								 | 
							
								    can also be used directly, and in some cases can be useful.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    To use :class:`_orm.Load` directly, instantiate it with the target mapped
							 | 
						||
| 
								 | 
							
								    class as the argument.   This style of usage is
							 | 
						||
| 
								 | 
							
								    useful when dealing with a :class:`_query.Query`
							 | 
						||
| 
								 | 
							
								    that has multiple entities::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        myopt = Load(MyClass).joinedload("widgets")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The above ``myopt`` can now be used with :meth:`_query.Query.options`,
							 | 
						||
| 
								 | 
							
								    where it
							 | 
						||
| 
								 | 
							
								    will only take effect for the ``MyClass`` entity::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass, MyOtherClass).options(myopt)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    One case where :class:`_orm.Load`
							 | 
						||
| 
								 | 
							
								    is useful as public API is when specifying
							 | 
						||
| 
								 | 
							
								    "wildcard" options that only take effect for a certain class::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(Order).options(Load(Order).lazyload('*'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Above, all relationships on ``Order`` will be lazy-loaded, but other
							 | 
						||
| 
								 | 
							
								    attributes on those descendant objects will load using their normal
							 | 
						||
| 
								 | 
							
								    loader strategy.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`deferred_options`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`deferred_loading_w_multiple`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`relationship_loader_options`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _is_strategy_option = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _cache_key_traversal = [
							 | 
						||
| 
								 | 
							
								        ("path", visitors.ExtendedInternalTraversal.dp_has_cache_key),
							 | 
						||
| 
								 | 
							
								        ("strategy", visitors.ExtendedInternalTraversal.dp_plain_obj),
							 | 
						||
| 
								 | 
							
								        ("_of_type", visitors.ExtendedInternalTraversal.dp_multi),
							 | 
						||
| 
								 | 
							
								        ("_extra_criteria", visitors.InternalTraversal.dp_clauseelement_list),
							 | 
						||
| 
								 | 
							
								        (
							 | 
						||
| 
								 | 
							
								            "_context_cache_key",
							 | 
						||
| 
								 | 
							
								            visitors.ExtendedInternalTraversal.dp_has_cache_key_tuples,
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        (
							 | 
						||
| 
								 | 
							
								            "local_opts",
							 | 
						||
| 
								 | 
							
								            visitors.ExtendedInternalTraversal.dp_string_multi_dict,
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, entity):
							 | 
						||
| 
								 | 
							
								        insp = inspect(entity)
							 | 
						||
| 
								 | 
							
								        insp._post_inspect
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.path = insp._path_registry
							 | 
						||
| 
								 | 
							
								        # note that this .context is shared among all descendant
							 | 
						||
| 
								 | 
							
								        # Load objects
							 | 
						||
| 
								 | 
							
								        self.context = util.OrderedDict()
							 | 
						||
| 
								 | 
							
								        self.local_opts = {}
							 | 
						||
| 
								 | 
							
								        self.is_class_strategy = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def for_existing_path(cls, path):
							 | 
						||
| 
								 | 
							
								        load = cls.__new__(cls)
							 | 
						||
| 
								 | 
							
								        load.path = path
							 | 
						||
| 
								 | 
							
								        load.context = {}
							 | 
						||
| 
								 | 
							
								        load.local_opts = {}
							 | 
						||
| 
								 | 
							
								        load._of_type = None
							 | 
						||
| 
								 | 
							
								        load._extra_criteria = ()
							 | 
						||
| 
								 | 
							
								        return load
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _generate_extra_criteria(self, context):
							 | 
						||
| 
								 | 
							
								        """Apply the current bound parameters in a QueryContext to the
							 | 
						||
| 
								 | 
							
								        immediate "extra_criteria" stored with this Load object.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Load objects are typically pulled from the cached version of
							 | 
						||
| 
								 | 
							
								        the statement from a QueryContext.  The statement currently being
							 | 
						||
| 
								 | 
							
								        executed will have new values (and keys) for bound parameters in the
							 | 
						||
| 
								 | 
							
								        extra criteria which need to be applied by loader strategies when
							 | 
						||
| 
								 | 
							
								        they handle this criteria for a result set.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        assert (
							 | 
						||
| 
								 | 
							
								            self._extra_criteria
							 | 
						||
| 
								 | 
							
								        ), "this should only be called if _extra_criteria is present"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        orig_query = context.compile_state.select_statement
							 | 
						||
| 
								 | 
							
								        current_query = context.query
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # NOTE: while it seems like we should not do the "apply" operation
							 | 
						||
| 
								 | 
							
								        # here if orig_query is current_query, skipping it in the "optimized"
							 | 
						||
| 
								 | 
							
								        # case causes the query to be different from a cache key perspective,
							 | 
						||
| 
								 | 
							
								        # because we are creating a copy of the criteria which is no longer
							 | 
						||
| 
								 | 
							
								        # the same identity of the _extra_criteria in the loader option
							 | 
						||
| 
								 | 
							
								        # itself.  cache key logic produces a different key for
							 | 
						||
| 
								 | 
							
								        # (A, copy_of_A) vs. (A, A), because in the latter case it shortens
							 | 
						||
| 
								 | 
							
								        # the second part of the key to just indicate on identity.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # if orig_query is current_query:
							 | 
						||
| 
								 | 
							
								        # not cached yet.   just do the and_()
							 | 
						||
| 
								 | 
							
								        #    return and_(*self._extra_criteria)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        k1 = orig_query._generate_cache_key()
							 | 
						||
| 
								 | 
							
								        k2 = current_query._generate_cache_key()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return k2._apply_params_to_element(k1, and_(*self._extra_criteria))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _adjust_for_extra_criteria(self, context):
							 | 
						||
| 
								 | 
							
								        """Apply the current bound parameters in a QueryContext to all
							 | 
						||
| 
								 | 
							
								        occurrences "extra_criteria" stored within al this Load object;
							 | 
						||
| 
								 | 
							
								        copying in place.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        orig_query = context.compile_state.select_statement
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        applied = {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        ck = [None, None]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def process(opt):
							 | 
						||
| 
								 | 
							
								            if not opt._extra_criteria:
							 | 
						||
| 
								 | 
							
								                return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if ck[0] is None:
							 | 
						||
| 
								 | 
							
								                ck[:] = (
							 | 
						||
| 
								 | 
							
								                    orig_query._generate_cache_key(),
							 | 
						||
| 
								 | 
							
								                    context.query._generate_cache_key(),
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            k1, k2 = ck
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            opt._extra_criteria = tuple(
							 | 
						||
| 
								 | 
							
								                k2._apply_params_to_element(k1, crit)
							 | 
						||
| 
								 | 
							
								                for crit in opt._extra_criteria
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self._deep_clone(applied, process)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _deep_clone(self, applied, process):
							 | 
						||
| 
								 | 
							
								        if self in applied:
							 | 
						||
| 
								 | 
							
								            return applied[self]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned = self._generate()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        applied[self] = cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned.strategy = self.strategy
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        assert cloned.propagate_to_loaders == self.propagate_to_loaders
							 | 
						||
| 
								 | 
							
								        assert cloned.is_class_strategy == self.is_class_strategy
							 | 
						||
| 
								 | 
							
								        assert cloned.is_opts_only == self.is_opts_only
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.context:
							 | 
						||
| 
								 | 
							
								            cloned.context = util.OrderedDict(
							 | 
						||
| 
								 | 
							
								                [
							 | 
						||
| 
								 | 
							
								                    (
							 | 
						||
| 
								 | 
							
								                        key,
							 | 
						||
| 
								 | 
							
								                        value._deep_clone(applied, process)
							 | 
						||
| 
								 | 
							
								                        if isinstance(value, Load)
							 | 
						||
| 
								 | 
							
								                        else value,
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                    for key, value in self.context.items()
							 | 
						||
| 
								 | 
							
								                ]
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned.local_opts.update(self.local_opts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        process(cloned)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _context_cache_key(self):
							 | 
						||
| 
								 | 
							
								        serialized = []
							 | 
						||
| 
								 | 
							
								        if self.context is None:
							 | 
						||
| 
								 | 
							
								            return []
							 | 
						||
| 
								 | 
							
								        for (key, loader_path), obj in self.context.items():
							 | 
						||
| 
								 | 
							
								            if key != "loader":
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            serialized.append(loader_path + (obj,))
							 | 
						||
| 
								 | 
							
								        return serialized
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _generate(self):
							 | 
						||
| 
								 | 
							
								        cloned = super(Load, self)._generate()
							 | 
						||
| 
								 | 
							
								        cloned.local_opts = {}
							 | 
						||
| 
								 | 
							
								        return cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    is_opts_only = False
							 | 
						||
| 
								 | 
							
								    is_class_strategy = False
							 | 
						||
| 
								 | 
							
								    strategy = None
							 | 
						||
| 
								 | 
							
								    propagate_to_loaders = False
							 | 
						||
| 
								 | 
							
								    _of_type = None
							 | 
						||
| 
								 | 
							
								    _extra_criteria = ()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def process_compile_state_replaced_entities(
							 | 
						||
| 
								 | 
							
								        self, compile_state, mapper_entities
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        if not compile_state.compile_options._enable_eagerloads:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # process is being run here so that the options given are validated
							 | 
						||
| 
								 | 
							
								        # against what the lead entities were, as well as to accommodate
							 | 
						||
| 
								 | 
							
								        # for the entities having been replaced with equivalents
							 | 
						||
| 
								 | 
							
								        self._process(
							 | 
						||
| 
								 | 
							
								            compile_state,
							 | 
						||
| 
								 | 
							
								            mapper_entities,
							 | 
						||
| 
								 | 
							
								            not bool(compile_state.current_path),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def process_compile_state(self, compile_state):
							 | 
						||
| 
								 | 
							
								        if not compile_state.compile_options._enable_eagerloads:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._process(
							 | 
						||
| 
								 | 
							
								            compile_state,
							 | 
						||
| 
								 | 
							
								            compile_state._lead_mapper_entities,
							 | 
						||
| 
								 | 
							
								            not bool(compile_state.current_path)
							 | 
						||
| 
								 | 
							
								            and not compile_state.compile_options._for_refresh_state,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _process(self, compile_state, mapper_entities, raiseerr):
							 | 
						||
| 
								 | 
							
								        is_refresh = compile_state.compile_options._for_refresh_state
							 | 
						||
| 
								 | 
							
								        current_path = compile_state.current_path
							 | 
						||
| 
								 | 
							
								        if current_path:
							 | 
						||
| 
								 | 
							
								            for (token, start_path), loader in self.context.items():
							 | 
						||
| 
								 | 
							
								                if is_refresh and not loader.propagate_to_loaders:
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                chopped_start_path = self._chop_path(start_path, current_path)
							 | 
						||
| 
								 | 
							
								                if chopped_start_path is not None:
							 | 
						||
| 
								 | 
							
								                    compile_state.attributes[
							 | 
						||
| 
								 | 
							
								                        (token, chopped_start_path)
							 | 
						||
| 
								 | 
							
								                    ] = loader
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            compile_state.attributes.update(self.context)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _generate_path(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        path,
							 | 
						||
| 
								 | 
							
								        attr,
							 | 
						||
| 
								 | 
							
								        for_strategy,
							 | 
						||
| 
								 | 
							
								        wildcard_key,
							 | 
						||
| 
								 | 
							
								        raiseerr=True,
							 | 
						||
| 
								 | 
							
								        polymorphic_entity_context=None,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        existing_of_type = self._of_type
							 | 
						||
| 
								 | 
							
								        self._of_type = None
							 | 
						||
| 
								 | 
							
								        if raiseerr and not path.has_entity:
							 | 
						||
| 
								 | 
							
								            if isinstance(path, TokenRegistry):
							 | 
						||
| 
								 | 
							
								                raise sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                    "Wildcard token cannot be followed by another entity"
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                raise sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                    "Mapped attribute '%s' does not "
							 | 
						||
| 
								 | 
							
								                    "refer to a mapped entity" % (path.prop,)
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if isinstance(attr, util.string_types):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            default_token = attr.endswith(_DEFAULT_TOKEN)
							 | 
						||
| 
								 | 
							
								            attr_str_name = attr
							 | 
						||
| 
								 | 
							
								            if attr.endswith(_WILDCARD_TOKEN) or default_token:
							 | 
						||
| 
								 | 
							
								                if default_token:
							 | 
						||
| 
								 | 
							
								                    self.propagate_to_loaders = False
							 | 
						||
| 
								 | 
							
								                if wildcard_key:
							 | 
						||
| 
								 | 
							
								                    attr = "%s:%s" % (wildcard_key, attr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # TODO: AliasedInsp inside the path for of_type is not
							 | 
						||
| 
								 | 
							
								                # working for a with_polymorphic entity because the
							 | 
						||
| 
								 | 
							
								                # relationship loaders don't render the with_poly into the
							 | 
						||
| 
								 | 
							
								                # path.  See #4469 which will try to improve this
							 | 
						||
| 
								 | 
							
								                if existing_of_type and not existing_of_type.is_aliased_class:
							 | 
						||
| 
								 | 
							
								                    path = path.parent[existing_of_type]
							 | 
						||
| 
								 | 
							
								                path = path.token(attr)
							 | 
						||
| 
								 | 
							
								                self.path = path
							 | 
						||
| 
								 | 
							
								                return path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if existing_of_type:
							 | 
						||
| 
								 | 
							
								                ent = inspect(existing_of_type)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                ent = path.entity
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            util.warn_deprecated_20(
							 | 
						||
| 
								 | 
							
								                "Using strings to indicate column or "
							 | 
						||
| 
								 | 
							
								                "relationship paths in loader options is deprecated "
							 | 
						||
| 
								 | 
							
								                "and will be removed in SQLAlchemy 2.0.  Please use "
							 | 
						||
| 
								 | 
							
								                "the class-bound attribute directly.",
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                # use getattr on the class to work around
							 | 
						||
| 
								 | 
							
								                # synonyms, hybrids, etc.
							 | 
						||
| 
								 | 
							
								                attr = getattr(ent.class_, attr)
							 | 
						||
| 
								 | 
							
								            except AttributeError as err:
							 | 
						||
| 
								 | 
							
								                if raiseerr:
							 | 
						||
| 
								 | 
							
								                    util.raise_(
							 | 
						||
| 
								 | 
							
								                        sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                            'Can\'t find property named "%s" on '
							 | 
						||
| 
								 | 
							
								                            "%s in this Query." % (attr, ent)
							 | 
						||
| 
								 | 
							
								                        ),
							 | 
						||
| 
								 | 
							
								                        replace_context=err,
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    return None
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    attr = found_property = attr.property
							 | 
						||
| 
								 | 
							
								                except AttributeError as ae:
							 | 
						||
| 
								 | 
							
								                    if not isinstance(attr, MapperProperty):
							 | 
						||
| 
								 | 
							
								                        util.raise_(
							 | 
						||
| 
								 | 
							
								                            sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                                'Expected attribute "%s" on %s to be a '
							 | 
						||
| 
								 | 
							
								                                "mapped attribute; "
							 | 
						||
| 
								 | 
							
								                                "instead got %s object."
							 | 
						||
| 
								 | 
							
								                                % (attr_str_name, ent, type(attr))
							 | 
						||
| 
								 | 
							
								                            ),
							 | 
						||
| 
								 | 
							
								                            replace_context=ae,
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            path = path[attr]
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            insp = inspect(attr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if insp.is_mapper or insp.is_aliased_class:
							 | 
						||
| 
								 | 
							
								                # TODO: this does not appear to be a valid codepath.  "attr"
							 | 
						||
| 
								 | 
							
								                # would never be a mapper.  This block is present in 1.2
							 | 
						||
| 
								 | 
							
								                # as well however does not seem to be accessed in any tests.
							 | 
						||
| 
								 | 
							
								                if not orm_util._entity_corresponds_to_use_path_impl(
							 | 
						||
| 
								 | 
							
								                    attr.parent, path[-1]
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    if raiseerr:
							 | 
						||
| 
								 | 
							
								                        raise sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                            "Attribute '%s' does not "
							 | 
						||
| 
								 | 
							
								                            "link from element '%s'" % (attr, path.entity)
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        return None
							 | 
						||
| 
								 | 
							
								            elif insp.is_property:
							 | 
						||
| 
								 | 
							
								                prop = found_property = attr
							 | 
						||
| 
								 | 
							
								                path = path[prop]
							 | 
						||
| 
								 | 
							
								            elif insp.is_attribute:
							 | 
						||
| 
								 | 
							
								                prop = found_property = attr.property
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if not orm_util._entity_corresponds_to_use_path_impl(
							 | 
						||
| 
								 | 
							
								                    attr.parent, path[-1]
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    if raiseerr:
							 | 
						||
| 
								 | 
							
								                        raise sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                            'Attribute "%s" does not '
							 | 
						||
| 
								 | 
							
								                            'link from element "%s".%s'
							 | 
						||
| 
								 | 
							
								                            % (
							 | 
						||
| 
								 | 
							
								                                attr,
							 | 
						||
| 
								 | 
							
								                                path.entity,
							 | 
						||
| 
								 | 
							
								                                (
							 | 
						||
| 
								 | 
							
								                                    "  Did you mean to use "
							 | 
						||
| 
								 | 
							
								                                    "%s.of_type(%s)?"
							 | 
						||
| 
								 | 
							
								                                    % (path[-2], attr.class_.__name__)
							 | 
						||
| 
								 | 
							
								                                    if len(path) > 1
							 | 
						||
| 
								 | 
							
								                                    and path.entity.is_mapper
							 | 
						||
| 
								 | 
							
								                                    and attr.parent.is_aliased_class
							 | 
						||
| 
								 | 
							
								                                    else ""
							 | 
						||
| 
								 | 
							
								                                ),
							 | 
						||
| 
								 | 
							
								                            )
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if attr._extra_criteria and not self._extra_criteria:
							 | 
						||
| 
								 | 
							
								                    # in most cases, the process that brings us here will have
							 | 
						||
| 
								 | 
							
								                    # already established _extra_criteria.  however if not,
							 | 
						||
| 
								 | 
							
								                    # and it's present on the attribute, then use that.
							 | 
						||
| 
								 | 
							
								                    self._extra_criteria = attr._extra_criteria
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if getattr(attr, "_of_type", None):
							 | 
						||
| 
								 | 
							
								                    ac = attr._of_type
							 | 
						||
| 
								 | 
							
								                    ext_info = of_type_info = inspect(ac)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if polymorphic_entity_context is None:
							 | 
						||
| 
								 | 
							
								                        polymorphic_entity_context = self.context
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    existing = path.entity_path[prop].get(
							 | 
						||
| 
								 | 
							
								                        polymorphic_entity_context, "path_with_polymorphic"
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if not ext_info.is_aliased_class:
							 | 
						||
| 
								 | 
							
								                        ac = orm_util.with_polymorphic(
							 | 
						||
| 
								 | 
							
								                            ext_info.mapper.base_mapper,
							 | 
						||
| 
								 | 
							
								                            ext_info.mapper,
							 | 
						||
| 
								 | 
							
								                            aliased=True,
							 | 
						||
| 
								 | 
							
								                            _use_mapper_path=True,
							 | 
						||
| 
								 | 
							
								                            _existing_alias=inspect(existing)
							 | 
						||
| 
								 | 
							
								                            if existing is not None
							 | 
						||
| 
								 | 
							
								                            else None,
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        ext_info = inspect(ac)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    path.entity_path[prop].set(
							 | 
						||
| 
								 | 
							
								                        polymorphic_entity_context, "path_with_polymorphic", ac
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    path = path[prop][ext_info]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    self._of_type = of_type_info
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    path = path[prop]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if for_strategy is not None:
							 | 
						||
| 
								 | 
							
								            found_property._get_strategy(for_strategy)
							 | 
						||
| 
								 | 
							
								        if path.has_entity:
							 | 
						||
| 
								 | 
							
								            path = path.entity_path
							 | 
						||
| 
								 | 
							
								        self.path = path
							 | 
						||
| 
								 | 
							
								        return path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __str__(self):
							 | 
						||
| 
								 | 
							
								        return "Load(strategy=%r)" % (self.strategy,)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _coerce_strat(self, strategy):
							 | 
						||
| 
								 | 
							
								        if strategy is not None:
							 | 
						||
| 
								 | 
							
								            strategy = tuple(sorted(strategy.items()))
							 | 
						||
| 
								 | 
							
								        return strategy
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _apply_to_parent(self, parent, applied, bound):
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError(
							 | 
						||
| 
								 | 
							
								            "Only 'unbound' loader options may be used with the "
							 | 
						||
| 
								 | 
							
								            "Load.options() method"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_generative
							 | 
						||
| 
								 | 
							
								    def options(self, *opts):
							 | 
						||
| 
								 | 
							
								        r"""Apply a series of options as sub-options to this
							 | 
						||
| 
								 | 
							
								        :class:`_orm.Load`
							 | 
						||
| 
								 | 
							
								        object.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        E.g.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            query = session.query(Author)
							 | 
						||
| 
								 | 
							
								            query = query.options(
							 | 
						||
| 
								 | 
							
								                        joinedload(Author.book).options(
							 | 
						||
| 
								 | 
							
								                            load_only(Book.summary, Book.excerpt),
							 | 
						||
| 
								 | 
							
								                            joinedload(Book.citations).options(
							 | 
						||
| 
								 | 
							
								                                joinedload(Citation.author)
							 | 
						||
| 
								 | 
							
								                            )
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param \*opts: A series of loader option objects (ultimately
							 | 
						||
| 
								 | 
							
								         :class:`_orm.Load` objects) which should be applied to the path
							 | 
						||
| 
								 | 
							
								         specified by this :class:`_orm.Load` object.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.3.6
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            :func:`.defaultload`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            :ref:`relationship_loader_options`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            :ref:`deferred_loading_w_multiple`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        apply_cache = {}
							 | 
						||
| 
								 | 
							
								        bound = not isinstance(self, _UnboundLoad)
							 | 
						||
| 
								 | 
							
								        if bound:
							 | 
						||
| 
								 | 
							
								            raise NotImplementedError(
							 | 
						||
| 
								 | 
							
								                "The options() method is currently only supported "
							 | 
						||
| 
								 | 
							
								                "for 'unbound' loader options"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        for opt in opts:
							 | 
						||
| 
								 | 
							
								            opt._apply_to_parent(self, apply_cache, bound)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_generative
							 | 
						||
| 
								 | 
							
								    def set_relationship_strategy(
							 | 
						||
| 
								 | 
							
								        self, attr, strategy, propagate_to_loaders=True
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        strategy = self._coerce_strat(strategy)
							 | 
						||
| 
								 | 
							
								        self.propagate_to_loaders = propagate_to_loaders
							 | 
						||
| 
								 | 
							
								        cloned = self._clone_for_bind_strategy(attr, strategy, "relationship")
							 | 
						||
| 
								 | 
							
								        self.path = cloned.path
							 | 
						||
| 
								 | 
							
								        self._of_type = cloned._of_type
							 | 
						||
| 
								 | 
							
								        self._extra_criteria = cloned._extra_criteria
							 | 
						||
| 
								 | 
							
								        cloned.is_class_strategy = self.is_class_strategy = False
							 | 
						||
| 
								 | 
							
								        self.propagate_to_loaders = cloned.propagate_to_loaders
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_generative
							 | 
						||
| 
								 | 
							
								    def set_column_strategy(self, attrs, strategy, opts=None, opts_only=False):
							 | 
						||
| 
								 | 
							
								        strategy = self._coerce_strat(strategy)
							 | 
						||
| 
								 | 
							
								        self.is_class_strategy = False
							 | 
						||
| 
								 | 
							
								        for attr in attrs:
							 | 
						||
| 
								 | 
							
								            cloned = self._clone_for_bind_strategy(
							 | 
						||
| 
								 | 
							
								                attr, strategy, "column", opts_only=opts_only, opts=opts
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            cloned.propagate_to_loaders = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_generative
							 | 
						||
| 
								 | 
							
								    def set_generic_strategy(self, attrs, strategy):
							 | 
						||
| 
								 | 
							
								        strategy = self._coerce_strat(strategy)
							 | 
						||
| 
								 | 
							
								        for attr in attrs:
							 | 
						||
| 
								 | 
							
								            cloned = self._clone_for_bind_strategy(attr, strategy, None)
							 | 
						||
| 
								 | 
							
								            cloned.propagate_to_loaders = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @_generative
							 | 
						||
| 
								 | 
							
								    def set_class_strategy(self, strategy, opts):
							 | 
						||
| 
								 | 
							
								        strategy = self._coerce_strat(strategy)
							 | 
						||
| 
								 | 
							
								        cloned = self._clone_for_bind_strategy(None, strategy, None)
							 | 
						||
| 
								 | 
							
								        cloned.is_class_strategy = True
							 | 
						||
| 
								 | 
							
								        cloned.propagate_to_loaders = True
							 | 
						||
| 
								 | 
							
								        cloned.local_opts.update(opts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _clone_for_bind_strategy(
							 | 
						||
| 
								 | 
							
								        self, attr, strategy, wildcard_key, opts_only=False, opts=None
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        """Create an anonymous clone of the Load/_UnboundLoad that is suitable
							 | 
						||
| 
								 | 
							
								        to be placed in the context / _to_bind collection of this Load
							 | 
						||
| 
								 | 
							
								        object.   The clone will then lose references to context/_to_bind
							 | 
						||
| 
								 | 
							
								        in order to not create reference cycles.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        cloned = self._generate()
							 | 
						||
| 
								 | 
							
								        cloned._generate_path(self.path, attr, strategy, wildcard_key)
							 | 
						||
| 
								 | 
							
								        cloned.strategy = strategy
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned.local_opts = self.local_opts
							 | 
						||
| 
								 | 
							
								        if opts:
							 | 
						||
| 
								 | 
							
								            cloned.local_opts.update(opts)
							 | 
						||
| 
								 | 
							
								        if opts_only:
							 | 
						||
| 
								 | 
							
								            cloned.is_opts_only = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if strategy or cloned.is_opts_only:
							 | 
						||
| 
								 | 
							
								            cloned._set_path_strategy()
							 | 
						||
| 
								 | 
							
								        return cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _set_for_path(self, context, path, replace=True, merge_opts=False):
							 | 
						||
| 
								 | 
							
								        if merge_opts or not replace:
							 | 
						||
| 
								 | 
							
								            existing = path.get(context, "loader")
							 | 
						||
| 
								 | 
							
								            if existing:
							 | 
						||
| 
								 | 
							
								                if merge_opts:
							 | 
						||
| 
								 | 
							
								                    existing.local_opts.update(self.local_opts)
							 | 
						||
| 
								 | 
							
								                    existing._extra_criteria += self._extra_criteria
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                path.set(context, "loader", self)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            existing = path.get(context, "loader")
							 | 
						||
| 
								 | 
							
								            path.set(context, "loader", self)
							 | 
						||
| 
								 | 
							
								            if existing and existing.is_opts_only:
							 | 
						||
| 
								 | 
							
								                self.local_opts.update(existing.local_opts)
							 | 
						||
| 
								 | 
							
								                existing._extra_criteria += self._extra_criteria
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _set_path_strategy(self):
							 | 
						||
| 
								 | 
							
								        if not self.is_class_strategy and self.path.has_entity:
							 | 
						||
| 
								 | 
							
								            effective_path = self.path.parent
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            effective_path = self.path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if effective_path.is_token:
							 | 
						||
| 
								 | 
							
								            for path in effective_path.generate_for_superclasses():
							 | 
						||
| 
								 | 
							
								                self._set_for_path(
							 | 
						||
| 
								 | 
							
								                    self.context,
							 | 
						||
| 
								 | 
							
								                    path,
							 | 
						||
| 
								 | 
							
								                    replace=True,
							 | 
						||
| 
								 | 
							
								                    merge_opts=self.is_opts_only,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self._set_for_path(
							 | 
						||
| 
								 | 
							
								                self.context,
							 | 
						||
| 
								 | 
							
								                effective_path,
							 | 
						||
| 
								 | 
							
								                replace=True,
							 | 
						||
| 
								 | 
							
								                merge_opts=self.is_opts_only,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # remove cycles; _set_path_strategy is always invoked on an
							 | 
						||
| 
								 | 
							
								        # anonymous clone of the Load / UnboundLoad object since #5056
							 | 
						||
| 
								 | 
							
								        self.context = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __getstate__(self):
							 | 
						||
| 
								 | 
							
								        d = self.__dict__.copy()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # can't pickle this right now; warning is raised by strategies
							 | 
						||
| 
								 | 
							
								        d["_extra_criteria"] = ()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if d["context"] is not None:
							 | 
						||
| 
								 | 
							
								            d["context"] = PathRegistry.serialize_context_dict(
							 | 
						||
| 
								 | 
							
								                d["context"], ("loader",)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        d["path"] = self.path.serialize()
							 | 
						||
| 
								 | 
							
								        return d
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __setstate__(self, state):
							 | 
						||
| 
								 | 
							
								        self.__dict__.update(state)
							 | 
						||
| 
								 | 
							
								        self.path = PathRegistry.deserialize(self.path)
							 | 
						||
| 
								 | 
							
								        if self.context is not None:
							 | 
						||
| 
								 | 
							
								            self.context = PathRegistry.deserialize_context_dict(self.context)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _chop_path(self, to_chop, path):
							 | 
						||
| 
								 | 
							
								        i = -1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for i, (c_token, p_token) in enumerate(zip(to_chop, path.path)):
							 | 
						||
| 
								 | 
							
								            if isinstance(c_token, util.string_types):
							 | 
						||
| 
								 | 
							
								                # TODO: this is approximated from the _UnboundLoad
							 | 
						||
| 
								 | 
							
								                # version and probably has issues, not fully covered.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if i == 0 and c_token.endswith(":" + _DEFAULT_TOKEN):
							 | 
						||
| 
								 | 
							
								                    return to_chop
							 | 
						||
| 
								 | 
							
								                elif (
							 | 
						||
| 
								 | 
							
								                    c_token != "relationship:%s" % (_WILDCARD_TOKEN,)
							 | 
						||
| 
								 | 
							
								                    and c_token != p_token.key
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if c_token is p_token:
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            elif (
							 | 
						||
| 
								 | 
							
								                isinstance(c_token, InspectionAttr)
							 | 
						||
| 
								 | 
							
								                and c_token.is_mapper
							 | 
						||
| 
								 | 
							
								                and p_token.is_mapper
							 | 
						||
| 
								 | 
							
								                and c_token.isa(p_token)
							 | 
						||
| 
								 | 
							
								            ):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return None
							 | 
						||
| 
								 | 
							
								        return to_chop[i + 1 :]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _UnboundLoad(Load):
							 | 
						||
| 
								 | 
							
								    """Represent a loader option that isn't tied to a root entity.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The loader option will produce an entity-linked :class:`_orm.Load`
							 | 
						||
| 
								 | 
							
								    object when it is passed :meth:`_query.Query.options`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This provides compatibility with the traditional system
							 | 
						||
| 
								 | 
							
								    of freestanding options, e.g. ``joinedload('x.y.z')``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self):
							 | 
						||
| 
								 | 
							
								        self.path = ()
							 | 
						||
| 
								 | 
							
								        self._to_bind = []
							 | 
						||
| 
								 | 
							
								        self.local_opts = {}
							 | 
						||
| 
								 | 
							
								        self._extra_criteria = ()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _gen_cache_key(self, anon_map, bindparams, _unbound_option_seen=None):
							 | 
						||
| 
								 | 
							
								        """Inlined gen_cache_key
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Original traversal is::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            _cache_key_traversal = [
							 | 
						||
| 
								 | 
							
								                ("path", visitors.ExtendedInternalTraversal.dp_multi_list),
							 | 
						||
| 
								 | 
							
								                ("strategy", visitors.ExtendedInternalTraversal.dp_plain_obj),
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    "_to_bind",
							 | 
						||
| 
								 | 
							
								                    visitors.ExtendedInternalTraversal.dp_has_cache_key_list,
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    "_extra_criteria",
							 | 
						||
| 
								 | 
							
								                    visitors.InternalTraversal.dp_clauseelement_list),
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    "local_opts",
							 | 
						||
| 
								 | 
							
								                    visitors.ExtendedInternalTraversal.dp_string_multi_dict,
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The inlining is so that the "_to_bind" list can be flattened to not
							 | 
						||
| 
								 | 
							
								        repeat the same UnboundLoad options over and over again.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        See #6869
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        idself = id(self)
							 | 
						||
| 
								 | 
							
								        cls = self.__class__
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if idself in anon_map:
							 | 
						||
| 
								 | 
							
								            return (anon_map[idself], cls)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            id_ = anon_map[idself]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        vis = traversals._cache_key_traversal_visitor
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        seen = _unbound_option_seen
							 | 
						||
| 
								 | 
							
								        if seen is None:
							 | 
						||
| 
								 | 
							
								            seen = set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return (
							 | 
						||
| 
								 | 
							
								            (id_, cls)
							 | 
						||
| 
								 | 
							
								            + vis.visit_multi_list(
							 | 
						||
| 
								 | 
							
								                "path", self.path, self, anon_map, bindparams
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            + ("strategy", self.strategy)
							 | 
						||
| 
								 | 
							
								            + (
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    "_to_bind",
							 | 
						||
| 
								 | 
							
								                    tuple(
							 | 
						||
| 
								 | 
							
								                        elem._gen_cache_key(
							 | 
						||
| 
								 | 
							
								                            anon_map, bindparams, _unbound_option_seen=seen
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                        for elem in self._to_bind
							 | 
						||
| 
								 | 
							
								                        if elem not in seen and not seen.add(elem)
							 | 
						||
| 
								 | 
							
								                    ),
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                if self._to_bind
							 | 
						||
| 
								 | 
							
								                else ()
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            + (
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    "_extra_criteria",
							 | 
						||
| 
								 | 
							
								                    tuple(
							 | 
						||
| 
								 | 
							
								                        elem._gen_cache_key(anon_map, bindparams)
							 | 
						||
| 
								 | 
							
								                        for elem in self._extra_criteria
							 | 
						||
| 
								 | 
							
								                    ),
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                if self._extra_criteria
							 | 
						||
| 
								 | 
							
								                else ()
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            + (
							 | 
						||
| 
								 | 
							
								                vis.visit_string_multi_dict(
							 | 
						||
| 
								 | 
							
								                    "local_opts", self.local_opts, self, anon_map, bindparams
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                if self.local_opts
							 | 
						||
| 
								 | 
							
								                else ()
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _is_chain_link = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _set_path_strategy(self):
							 | 
						||
| 
								 | 
							
								        self._to_bind.append(self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # remove cycles; _set_path_strategy is always invoked on an
							 | 
						||
| 
								 | 
							
								        # anonymous clone of the Load / UnboundLoad object since #5056
							 | 
						||
| 
								 | 
							
								        self._to_bind = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _deep_clone(self, applied, process):
							 | 
						||
| 
								 | 
							
								        if self in applied:
							 | 
						||
| 
								 | 
							
								            return applied[self]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned = self._generate()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        applied[self] = cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned.strategy = self.strategy
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        assert cloned.propagate_to_loaders == self.propagate_to_loaders
							 | 
						||
| 
								 | 
							
								        assert cloned.is_class_strategy == self.is_class_strategy
							 | 
						||
| 
								 | 
							
								        assert cloned.is_opts_only == self.is_opts_only
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned._to_bind = [
							 | 
						||
| 
								 | 
							
								            elem._deep_clone(applied, process) for elem in self._to_bind or ()
							 | 
						||
| 
								 | 
							
								        ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned.local_opts.update(self.local_opts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        process(cloned)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _apply_to_parent(self, parent, applied, bound, to_bind=None):
							 | 
						||
| 
								 | 
							
								        if self in applied:
							 | 
						||
| 
								 | 
							
								            return applied[self]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if to_bind is None:
							 | 
						||
| 
								 | 
							
								            to_bind = self._to_bind
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned = self._generate()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        applied[self] = cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned.strategy = self.strategy
							 | 
						||
| 
								 | 
							
								        if self.path:
							 | 
						||
| 
								 | 
							
								            attr = self.path[-1]
							 | 
						||
| 
								 | 
							
								            if isinstance(attr, util.string_types) and attr.endswith(
							 | 
						||
| 
								 | 
							
								                _DEFAULT_TOKEN
							 | 
						||
| 
								 | 
							
								            ):
							 | 
						||
| 
								 | 
							
								                attr = attr.split(":")[0] + ":" + _WILDCARD_TOKEN
							 | 
						||
| 
								 | 
							
								            cloned._generate_path(
							 | 
						||
| 
								 | 
							
								                parent.path + self.path[0:-1], attr, self.strategy, None
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # these assertions can go away once the "sub options" API is
							 | 
						||
| 
								 | 
							
								        # mature
							 | 
						||
| 
								 | 
							
								        assert cloned.propagate_to_loaders == self.propagate_to_loaders
							 | 
						||
| 
								 | 
							
								        assert cloned.is_class_strategy == self.is_class_strategy
							 | 
						||
| 
								 | 
							
								        assert cloned.is_opts_only == self.is_opts_only
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        uniq = set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned._to_bind = parent._to_bind
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned._to_bind[:] = [
							 | 
						||
| 
								 | 
							
								            elem
							 | 
						||
| 
								 | 
							
								            for elem in cloned._to_bind
							 | 
						||
| 
								 | 
							
								            if elem not in uniq and not uniq.add(elem)
							 | 
						||
| 
								 | 
							
								        ] + [
							 | 
						||
| 
								 | 
							
								            elem._apply_to_parent(parent, applied, bound, to_bind)
							 | 
						||
| 
								 | 
							
								            for elem in to_bind
							 | 
						||
| 
								 | 
							
								            if elem not in uniq and not uniq.add(elem)
							 | 
						||
| 
								 | 
							
								        ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cloned.local_opts.update(self.local_opts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _generate_path(self, path, attr, for_strategy, wildcard_key):
							 | 
						||
| 
								 | 
							
								        if (
							 | 
						||
| 
								 | 
							
								            wildcard_key
							 | 
						||
| 
								 | 
							
								            and isinstance(attr, util.string_types)
							 | 
						||
| 
								 | 
							
								            and attr in (_WILDCARD_TOKEN, _DEFAULT_TOKEN)
							 | 
						||
| 
								 | 
							
								        ):
							 | 
						||
| 
								 | 
							
								            if attr == _DEFAULT_TOKEN:
							 | 
						||
| 
								 | 
							
								                self.propagate_to_loaders = False
							 | 
						||
| 
								 | 
							
								            attr = "%s:%s" % (wildcard_key, attr)
							 | 
						||
| 
								 | 
							
								        if path and _is_mapped_class(path[-1]) and not self.is_class_strategy:
							 | 
						||
| 
								 | 
							
								            path = path[0:-1]
							 | 
						||
| 
								 | 
							
								        if attr:
							 | 
						||
| 
								 | 
							
								            path = path + (attr,)
							 | 
						||
| 
								 | 
							
								        self.path = path
							 | 
						||
| 
								 | 
							
								        self._extra_criteria = getattr(attr, "_extra_criteria", ())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __getstate__(self):
							 | 
						||
| 
								 | 
							
								        d = self.__dict__.copy()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # can't pickle this right now; warning is raised by strategies
							 | 
						||
| 
								 | 
							
								        d["_extra_criteria"] = ()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        d["path"] = self._serialize_path(self.path, filter_aliased_class=True)
							 | 
						||
| 
								 | 
							
								        return d
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __setstate__(self, state):
							 | 
						||
| 
								 | 
							
								        ret = []
							 | 
						||
| 
								 | 
							
								        for key in state["path"]:
							 | 
						||
| 
								 | 
							
								            if isinstance(key, tuple):
							 | 
						||
| 
								 | 
							
								                if len(key) == 2:
							 | 
						||
| 
								 | 
							
								                    # support legacy
							 | 
						||
| 
								 | 
							
								                    cls, propkey = key
							 | 
						||
| 
								 | 
							
								                    of_type = None
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    cls, propkey, of_type = key
							 | 
						||
| 
								 | 
							
								                prop = getattr(cls, propkey)
							 | 
						||
| 
								 | 
							
								                if of_type:
							 | 
						||
| 
								 | 
							
								                    prop = prop.of_type(of_type)
							 | 
						||
| 
								 | 
							
								                ret.append(prop)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                ret.append(key)
							 | 
						||
| 
								 | 
							
								        state["path"] = tuple(ret)
							 | 
						||
| 
								 | 
							
								        self.__dict__ = state
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _process(self, compile_state, mapper_entities, raiseerr):
							 | 
						||
| 
								 | 
							
								        dedupes = compile_state.attributes["_unbound_load_dedupes"]
							 | 
						||
| 
								 | 
							
								        is_refresh = compile_state.compile_options._for_refresh_state
							 | 
						||
| 
								 | 
							
								        for val in self._to_bind:
							 | 
						||
| 
								 | 
							
								            if val not in dedupes:
							 | 
						||
| 
								 | 
							
								                dedupes.add(val)
							 | 
						||
| 
								 | 
							
								                if is_refresh and not val.propagate_to_loaders:
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                val._bind_loader(
							 | 
						||
| 
								 | 
							
								                    [ent.entity_zero for ent in mapper_entities],
							 | 
						||
| 
								 | 
							
								                    compile_state.current_path,
							 | 
						||
| 
								 | 
							
								                    compile_state.attributes,
							 | 
						||
| 
								 | 
							
								                    raiseerr,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _from_keys(cls, meth, keys, chained, kw):
							 | 
						||
| 
								 | 
							
								        opt = _UnboundLoad()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def _split_key(key):
							 | 
						||
| 
								 | 
							
								            if isinstance(key, util.string_types):
							 | 
						||
| 
								 | 
							
								                # coerce fooload('*') into "default loader strategy"
							 | 
						||
| 
								 | 
							
								                if key == _WILDCARD_TOKEN:
							 | 
						||
| 
								 | 
							
								                    return (_DEFAULT_TOKEN,)
							 | 
						||
| 
								 | 
							
								                # coerce fooload(".*") into "wildcard on default entity"
							 | 
						||
| 
								 | 
							
								                elif key.startswith("." + _WILDCARD_TOKEN):
							 | 
						||
| 
								 | 
							
								                    util.warn_deprecated(
							 | 
						||
| 
								 | 
							
								                        "The undocumented `.{WILDCARD}` format is deprecated "
							 | 
						||
| 
								 | 
							
								                        "and will be removed in a future version as it is "
							 | 
						||
| 
								 | 
							
								                        "believed to be unused. "
							 | 
						||
| 
								 | 
							
								                        "If you have been using this functionality, please "
							 | 
						||
| 
								 | 
							
								                        "comment on Issue #4390 on the SQLAlchemy project "
							 | 
						||
| 
								 | 
							
								                        "tracker.",
							 | 
						||
| 
								 | 
							
								                        version="1.4",
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                    key = key[1:]
							 | 
						||
| 
								 | 
							
								                return key.split(".")
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return (key,)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        all_tokens = [token for key in keys for token in _split_key(key)]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for token in all_tokens[0:-1]:
							 | 
						||
| 
								 | 
							
								            # set _is_chain_link first so that clones of the
							 | 
						||
| 
								 | 
							
								            # object also inherit this flag
							 | 
						||
| 
								 | 
							
								            opt._is_chain_link = True
							 | 
						||
| 
								 | 
							
								            if chained:
							 | 
						||
| 
								 | 
							
								                opt = meth(opt, token, **kw)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                opt = opt.defaultload(token)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        opt = meth(opt, all_tokens[-1], **kw)
							 | 
						||
| 
								 | 
							
								        opt._is_chain_link = False
							 | 
						||
| 
								 | 
							
								        return opt
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _chop_path(self, to_chop, path):
							 | 
						||
| 
								 | 
							
								        i = -1
							 | 
						||
| 
								 | 
							
								        for i, (c_token, (p_entity, p_prop)) in enumerate(
							 | 
						||
| 
								 | 
							
								            zip(to_chop, path.pairs())
							 | 
						||
| 
								 | 
							
								        ):
							 | 
						||
| 
								 | 
							
								            if isinstance(c_token, util.string_types):
							 | 
						||
| 
								 | 
							
								                if i == 0 and c_token.endswith(":" + _DEFAULT_TOKEN):
							 | 
						||
| 
								 | 
							
								                    return to_chop
							 | 
						||
| 
								 | 
							
								                elif (
							 | 
						||
| 
								 | 
							
								                    c_token != "relationship:%s" % (_WILDCARD_TOKEN,)
							 | 
						||
| 
								 | 
							
								                    and c_token != p_prop.key
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    return None
							 | 
						||
| 
								 | 
							
								            elif isinstance(c_token, PropComparator):
							 | 
						||
| 
								 | 
							
								                if c_token.property is not p_prop or (
							 | 
						||
| 
								 | 
							
								                    c_token._parententity is not p_entity
							 | 
						||
| 
								 | 
							
								                    and (
							 | 
						||
| 
								 | 
							
								                        not c_token._parententity.is_mapper
							 | 
						||
| 
								 | 
							
								                        or not c_token._parententity.isa(p_entity)
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    return None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            i += 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return to_chop[i:]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _serialize_path(self, path, filter_aliased_class=False):
							 | 
						||
| 
								 | 
							
								        ret = []
							 | 
						||
| 
								 | 
							
								        for token in path:
							 | 
						||
| 
								 | 
							
								            if isinstance(token, QueryableAttribute):
							 | 
						||
| 
								 | 
							
								                if (
							 | 
						||
| 
								 | 
							
								                    filter_aliased_class
							 | 
						||
| 
								 | 
							
								                    and token._of_type
							 | 
						||
| 
								 | 
							
								                    and inspect(token._of_type).is_aliased_class
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    ret.append((token._parentmapper.class_, token.key, None))
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    ret.append(
							 | 
						||
| 
								 | 
							
								                        (
							 | 
						||
| 
								 | 
							
								                            token._parentmapper.class_,
							 | 
						||
| 
								 | 
							
								                            token.key,
							 | 
						||
| 
								 | 
							
								                            token._of_type.entity if token._of_type else None,
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								            elif isinstance(token, PropComparator):
							 | 
						||
| 
								 | 
							
								                ret.append((token._parentmapper.class_, token.key, None))
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                ret.append(token)
							 | 
						||
| 
								 | 
							
								        return ret
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _bind_loader(self, entities, current_path, context, raiseerr):
							 | 
						||
| 
								 | 
							
								        """Convert from an _UnboundLoad() object into a Load() object.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The _UnboundLoad() uses an informal "path" and does not necessarily
							 | 
						||
| 
								 | 
							
								        refer to a lead entity as it may use string tokens.   The Load()
							 | 
						||
| 
								 | 
							
								        OTOH refers to a complete path.   This method reconciles from a
							 | 
						||
| 
								 | 
							
								        given Query into a Load.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Example::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            query = session.query(User).options(
							 | 
						||
| 
								 | 
							
								                joinedload("orders").joinedload("items"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The above options will be an _UnboundLoad object along the lines
							 | 
						||
| 
								 | 
							
								        of (note this is not the exact API of _UnboundLoad)::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            _UnboundLoad(
							 | 
						||
| 
								 | 
							
								                _to_bind=[
							 | 
						||
| 
								 | 
							
								                    _UnboundLoad(["orders"], {"lazy": "joined"}),
							 | 
						||
| 
								 | 
							
								                    _UnboundLoad(["orders", "items"], {"lazy": "joined"}),
							 | 
						||
| 
								 | 
							
								                ]
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        After this method, we get something more like this (again this is
							 | 
						||
| 
								 | 
							
								        not exact API)::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            Load(
							 | 
						||
| 
								 | 
							
								                User,
							 | 
						||
| 
								 | 
							
								                (User, User.orders.property))
							 | 
						||
| 
								 | 
							
								            Load(
							 | 
						||
| 
								 | 
							
								                User,
							 | 
						||
| 
								 | 
							
								                (User, User.orders.property, Order, Order.items.property))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        start_path = self.path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.is_class_strategy and current_path:
							 | 
						||
| 
								 | 
							
								            start_path += (entities[0],)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # _current_path implies we're in a
							 | 
						||
| 
								 | 
							
								        # secondary load with an existing path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if current_path:
							 | 
						||
| 
								 | 
							
								            start_path = self._chop_path(start_path, current_path)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not start_path:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # look at the first token and try to locate within the Query
							 | 
						||
| 
								 | 
							
								        # what entity we are referring towards.
							 | 
						||
| 
								 | 
							
								        token = start_path[0]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if isinstance(token, util.string_types):
							 | 
						||
| 
								 | 
							
								            entity = self._find_entity_basestring(entities, token, raiseerr)
							 | 
						||
| 
								 | 
							
								        elif isinstance(token, PropComparator):
							 | 
						||
| 
								 | 
							
								            prop = token.property
							 | 
						||
| 
								 | 
							
								            entity = self._find_entity_prop_comparator(
							 | 
						||
| 
								 | 
							
								                entities, prop, token._parententity, raiseerr
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        elif self.is_class_strategy and _is_mapped_class(token):
							 | 
						||
| 
								 | 
							
								            entity = inspect(token)
							 | 
						||
| 
								 | 
							
								            if entity not in entities:
							 | 
						||
| 
								 | 
							
								                entity = None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                "mapper option expects " "string key or list of attributes"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not entity:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        path_element = entity
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # transfer our entity-less state into a Load() object
							 | 
						||
| 
								 | 
							
								        # with a real entity path.  Start with the lead entity
							 | 
						||
| 
								 | 
							
								        # we just located, then go through the rest of our path
							 | 
						||
| 
								 | 
							
								        # tokens and populate into the Load().
							 | 
						||
| 
								 | 
							
								        loader = Load(path_element)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if context is None:
							 | 
						||
| 
								 | 
							
								            context = loader.context
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        loader.strategy = self.strategy
							 | 
						||
| 
								 | 
							
								        loader.is_opts_only = self.is_opts_only
							 | 
						||
| 
								 | 
							
								        loader.is_class_strategy = self.is_class_strategy
							 | 
						||
| 
								 | 
							
								        loader._extra_criteria = self._extra_criteria
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        path = loader.path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not loader.is_class_strategy:
							 | 
						||
| 
								 | 
							
								            for idx, token in enumerate(start_path):
							 | 
						||
| 
								 | 
							
								                if not loader._generate_path(
							 | 
						||
| 
								 | 
							
								                    loader.path,
							 | 
						||
| 
								 | 
							
								                    token,
							 | 
						||
| 
								 | 
							
								                    self.strategy if idx == len(start_path) - 1 else None,
							 | 
						||
| 
								 | 
							
								                    None,
							 | 
						||
| 
								 | 
							
								                    raiseerr,
							 | 
						||
| 
								 | 
							
								                    polymorphic_entity_context=context,
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        loader.local_opts.update(self.local_opts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not loader.is_class_strategy and loader.path.has_entity:
							 | 
						||
| 
								 | 
							
								            effective_path = loader.path.parent
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            effective_path = loader.path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # prioritize "first class" options over those
							 | 
						||
| 
								 | 
							
								        # that were "links in the chain", e.g. "x" and "y" in
							 | 
						||
| 
								 | 
							
								        # someload("x.y.z") versus someload("x") / someload("x.y")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if effective_path.is_token:
							 | 
						||
| 
								 | 
							
								            for path in effective_path.generate_for_superclasses():
							 | 
						||
| 
								 | 
							
								                loader._set_for_path(
							 | 
						||
| 
								 | 
							
								                    context,
							 | 
						||
| 
								 | 
							
								                    path,
							 | 
						||
| 
								 | 
							
								                    replace=not self._is_chain_link,
							 | 
						||
| 
								 | 
							
								                    merge_opts=self.is_opts_only,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            loader._set_for_path(
							 | 
						||
| 
								 | 
							
								                context,
							 | 
						||
| 
								 | 
							
								                effective_path,
							 | 
						||
| 
								 | 
							
								                replace=not self._is_chain_link,
							 | 
						||
| 
								 | 
							
								                merge_opts=self.is_opts_only,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return loader
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _find_entity_prop_comparator(self, entities, prop, mapper, raiseerr):
							 | 
						||
| 
								 | 
							
								        if _is_aliased_class(mapper):
							 | 
						||
| 
								 | 
							
								            searchfor = mapper
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            searchfor = _class_to_mapper(mapper)
							 | 
						||
| 
								 | 
							
								        for ent in entities:
							 | 
						||
| 
								 | 
							
								            if orm_util._entity_corresponds_to(ent, searchfor):
							 | 
						||
| 
								 | 
							
								                return ent
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            if raiseerr:
							 | 
						||
| 
								 | 
							
								                if not list(entities):
							 | 
						||
| 
								 | 
							
								                    raise sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                        "Query has only expression-based entities, "
							 | 
						||
| 
								 | 
							
								                        'which do not apply to %s "%s"'
							 | 
						||
| 
								 | 
							
								                        % (util.clsname_as_plain_name(type(prop)), prop)
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    raise sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                        'Mapped attribute "%s" does not apply to any of the '
							 | 
						||
| 
								 | 
							
								                        "root entities in this query, e.g. %s. Please "
							 | 
						||
| 
								 | 
							
								                        "specify the full path "
							 | 
						||
| 
								 | 
							
								                        "from one of the root entities to the target "
							 | 
						||
| 
								 | 
							
								                        "attribute. "
							 | 
						||
| 
								 | 
							
								                        % (prop, ", ".join(str(x) for x in entities))
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _find_entity_basestring(self, entities, token, raiseerr):
							 | 
						||
| 
								 | 
							
								        if token.endswith(":" + _WILDCARD_TOKEN):
							 | 
						||
| 
								 | 
							
								            if len(list(entities)) != 1:
							 | 
						||
| 
								 | 
							
								                if raiseerr:
							 | 
						||
| 
								 | 
							
								                    raise sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                        "Can't apply wildcard ('*') or load_only() "
							 | 
						||
| 
								 | 
							
								                        "loader option to multiple entities %s. Specify "
							 | 
						||
| 
								 | 
							
								                        "loader options for each entity individually, such "
							 | 
						||
| 
								 | 
							
								                        "as %s."
							 | 
						||
| 
								 | 
							
								                        % (
							 | 
						||
| 
								 | 
							
								                            ", ".join(str(ent) for ent in entities),
							 | 
						||
| 
								 | 
							
								                            ", ".join(
							 | 
						||
| 
								 | 
							
								                                "Load(%s).some_option('*')" % ent
							 | 
						||
| 
								 | 
							
								                                for ent in entities
							 | 
						||
| 
								 | 
							
								                            ),
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								        elif token.endswith(_DEFAULT_TOKEN):
							 | 
						||
| 
								 | 
							
								            raiseerr = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for ent in entities:
							 | 
						||
| 
								 | 
							
								            # return only the first _MapperEntity when searching
							 | 
						||
| 
								 | 
							
								            # based on string prop name.   Ideally object
							 | 
						||
| 
								 | 
							
								            # attributes are used to specify more exactly.
							 | 
						||
| 
								 | 
							
								            return ent
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            if raiseerr:
							 | 
						||
| 
								 | 
							
								                raise sa_exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                    "Query has only expression-based entities - "
							 | 
						||
| 
								 | 
							
								                    'can\'t find property named "%s".' % (token,)
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class loader_option(object):
							 | 
						||
| 
								 | 
							
								    def __init__(self):
							 | 
						||
| 
								 | 
							
								        pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __call__(self, fn):
							 | 
						||
| 
								 | 
							
								        self.name = name = fn.__name__
							 | 
						||
| 
								 | 
							
								        self.fn = fn
							 | 
						||
| 
								 | 
							
								        if hasattr(Load, name):
							 | 
						||
| 
								 | 
							
								            raise TypeError("Load class already has a %s method." % (name))
							 | 
						||
| 
								 | 
							
								        setattr(Load, name, fn)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _add_unbound_fn(self, fn):
							 | 
						||
| 
								 | 
							
								        self._unbound_fn = fn
							 | 
						||
| 
								 | 
							
								        fn_doc = self.fn.__doc__
							 | 
						||
| 
								 | 
							
								        self.fn.__doc__ = """Produce a new :class:`_orm.Load` object with the
							 | 
						||
| 
								 | 
							
								:func:`_orm.%(name)s` option applied.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								See :func:`_orm.%(name)s` for usage examples.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								""" % {
							 | 
						||
| 
								 | 
							
								            "name": self.name
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        fn.__doc__ = fn_doc
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _add_unbound_all_fn(self, fn):
							 | 
						||
| 
								 | 
							
								        fn.__doc__ = """Produce a standalone "all" option for
							 | 
						||
| 
								 | 
							
								:func:`_orm.%(name)s`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. deprecated:: 0.9
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The :func:`_orm.%(name)s_all` function is deprecated, and will be removed
							 | 
						||
| 
								 | 
							
								    in a future release.  Please use method chaining with
							 | 
						||
| 
								 | 
							
								    :func:`_orm.%(name)s` instead, as in::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(
							 | 
						||
| 
								 | 
							
								            %(name)s("someattribute").%(name)s("anotherattribute")
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								""" % {
							 | 
						||
| 
								 | 
							
								            "name": self.name
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        fn = util.deprecated(
							 | 
						||
| 
								 | 
							
								            # This is used by `baked_lazyload_all` was only deprecated in
							 | 
						||
| 
								 | 
							
								            # version 1.2 so this must stick around until that is removed
							 | 
						||
| 
								 | 
							
								            "0.9",
							 | 
						||
| 
								 | 
							
								            "The :func:`.%(name)s_all` function is deprecated, and will be "
							 | 
						||
| 
								 | 
							
								            "removed in a future release.  Please use method chaining with "
							 | 
						||
| 
								 | 
							
								            ":func:`.%(name)s` instead" % {"name": self.name},
							 | 
						||
| 
								 | 
							
								            add_deprecation_to_docstring=False,
							 | 
						||
| 
								 | 
							
								        )(fn)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._unbound_all_fn = fn
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def contains_eager(loadopt, attr, alias=None):
							 | 
						||
| 
								 | 
							
								    r"""Indicate that the given attribute should be eagerly loaded from
							 | 
						||
| 
								 | 
							
								    columns stated manually in the query.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The option is used in conjunction with an explicit join that loads
							 | 
						||
| 
								 | 
							
								    the desired rows, i.e.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        sess.query(Order).\
							 | 
						||
| 
								 | 
							
								                join(Order.user).\
							 | 
						||
| 
								 | 
							
								                options(contains_eager(Order.user))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The above query would join from the ``Order`` entity to its related
							 | 
						||
| 
								 | 
							
								    ``User`` entity, and the returned ``Order`` objects would have the
							 | 
						||
| 
								 | 
							
								    ``Order.user`` attribute pre-populated.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    It may also be used for customizing the entries in an eagerly loaded
							 | 
						||
| 
								 | 
							
								    collection; queries will normally want to use the
							 | 
						||
| 
								 | 
							
								    :meth:`_query.Query.populate_existing` method assuming the primary
							 | 
						||
| 
								 | 
							
								    collection of parent objects may already have been loaded::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        sess.query(User).\
							 | 
						||
| 
								 | 
							
								            join(User.addresses).\
							 | 
						||
| 
								 | 
							
								            filter(Address.email_address.like('%@aol.com')).\
							 | 
						||
| 
								 | 
							
								            options(contains_eager(User.addresses)).\
							 | 
						||
| 
								 | 
							
								            populate_existing()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    See the section :ref:`contains_eager` for complete usage details.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`loading_toplevel`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`contains_eager`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    if alias is not None:
							 | 
						||
| 
								 | 
							
								        if not isinstance(alias, str):
							 | 
						||
| 
								 | 
							
								            info = inspect(alias)
							 | 
						||
| 
								 | 
							
								            alias = info.selectable
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            util.warn_deprecated(
							 | 
						||
| 
								 | 
							
								                "Passing a string name for the 'alias' argument to "
							 | 
						||
| 
								 | 
							
								                "'contains_eager()` is deprecated, and will not work in a "
							 | 
						||
| 
								 | 
							
								                "future release.  Please use a sqlalchemy.alias() or "
							 | 
						||
| 
								 | 
							
								                "sqlalchemy.orm.aliased() construct.",
							 | 
						||
| 
								 | 
							
								                version="1.4",
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    elif getattr(attr, "_of_type", None):
							 | 
						||
| 
								 | 
							
								        ot = inspect(attr._of_type)
							 | 
						||
| 
								 | 
							
								        alias = ot.selectable
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    cloned = loadopt.set_relationship_strategy(
							 | 
						||
| 
								 | 
							
								        attr, {"lazy": "joined"}, propagate_to_loaders=False
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    cloned.local_opts["eager_from_alias"] = alias
							 | 
						||
| 
								 | 
							
								    return cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@contains_eager._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def contains_eager(*keys, **kw):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad()._from_keys(
							 | 
						||
| 
								 | 
							
								        _UnboundLoad.contains_eager, keys, True, kw
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def load_only(loadopt, *attrs):
							 | 
						||
| 
								 | 
							
								    """Indicate that for a particular entity, only the given list
							 | 
						||
| 
								 | 
							
								    of column-based attribute names should be loaded; all others will be
							 | 
						||
| 
								 | 
							
								    deferred.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Example - given a class ``User``, load only the ``name`` and ``fullname``
							 | 
						||
| 
								 | 
							
								    attributes::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(User).options(load_only(User.name, User.fullname))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Example - given a relationship ``User.addresses -> Address``, specify
							 | 
						||
| 
								 | 
							
								    subquery loading for the ``User.addresses`` collection, but on each
							 | 
						||
| 
								 | 
							
								    ``Address`` object load only the ``email_address`` attribute::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(User).options(
							 | 
						||
| 
								 | 
							
								                subqueryload(User.addresses).load_only(Address.email_address)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    For a :class:`_query.Query` that has multiple entities,
							 | 
						||
| 
								 | 
							
								    the lead entity can be
							 | 
						||
| 
								 | 
							
								    specifically referred to using the :class:`_orm.Load` constructor::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(User, Address).join(User.addresses).options(
							 | 
						||
| 
								 | 
							
								                    Load(User).load_only(User.name, User.fullname),
							 | 
						||
| 
								 | 
							
								                    Load(Address).load_only(Address.email_address)
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     .. note:: This method will still load a :class:`_schema.Column` even
							 | 
						||
| 
								 | 
							
								        if the column property is defined with ``deferred=True``
							 | 
						||
| 
								 | 
							
								        for the :func:`.column_property` function.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionadded:: 0.9.0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    cloned = loadopt.set_column_strategy(
							 | 
						||
| 
								 | 
							
								        attrs, {"deferred": False, "instrument": True}
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    cloned.set_column_strategy(
							 | 
						||
| 
								 | 
							
								        "*", {"deferred": True, "instrument": True}, {"undefer_pks": True}
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    return cloned
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@load_only._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def load_only(*attrs):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad().load_only(*attrs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def joinedload(loadopt, attr, innerjoin=None):
							 | 
						||
| 
								 | 
							
								    """Indicate that the given attribute should be loaded using joined
							 | 
						||
| 
								 | 
							
								    eager loading.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    examples::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # joined-load the "orders" collection on "User"
							 | 
						||
| 
								 | 
							
								        query(User).options(joinedload(User.orders))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # joined-load Order.items and then Item.keywords
							 | 
						||
| 
								 | 
							
								        query(Order).options(
							 | 
						||
| 
								 | 
							
								            joinedload(Order.items).joinedload(Item.keywords))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # lazily load Order.items, but when Items are loaded,
							 | 
						||
| 
								 | 
							
								        # joined-load the keywords collection
							 | 
						||
| 
								 | 
							
								        query(Order).options(
							 | 
						||
| 
								 | 
							
								            lazyload(Order.items).joinedload(Item.keywords))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param innerjoin: if ``True``, indicates that the joined eager load should
							 | 
						||
| 
								 | 
							
								     use an inner join instead of the default of left outer join::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        query(Order).options(joinedload(Order.user, innerjoin=True))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     In order to chain multiple eager joins together where some may be
							 | 
						||
| 
								 | 
							
								     OUTER and others INNER, right-nested joins are used to link them::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        query(A).options(
							 | 
						||
| 
								 | 
							
								            joinedload(A.bs, innerjoin=False).
							 | 
						||
| 
								 | 
							
								                joinedload(B.cs, innerjoin=True)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     The above query, linking A.bs via "outer" join and B.cs via "inner" join
							 | 
						||
| 
								 | 
							
								     would render the joins as "a LEFT OUTER JOIN (b JOIN c)".   When using
							 | 
						||
| 
								 | 
							
								     older versions of SQLite (< 3.7.16), this form of JOIN is translated to
							 | 
						||
| 
								 | 
							
								     use full subqueries as this syntax is otherwise not directly supported.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     The ``innerjoin`` flag can also be stated with the term ``"unnested"``.
							 | 
						||
| 
								 | 
							
								     This indicates that an INNER JOIN should be used, *unless* the join
							 | 
						||
| 
								 | 
							
								     is linked to a LEFT OUTER JOIN to the left, in which case it
							 | 
						||
| 
								 | 
							
								     will render as LEFT OUTER JOIN.  For example, supposing ``A.bs``
							 | 
						||
| 
								 | 
							
								     is an outerjoin::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        query(A).options(
							 | 
						||
| 
								 | 
							
								            joinedload(A.bs).
							 | 
						||
| 
								 | 
							
								                joinedload(B.cs, innerjoin="unnested")
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     The above join will render as "a LEFT OUTER JOIN b LEFT OUTER JOIN c",
							 | 
						||
| 
								 | 
							
								     rather than as "a LEFT OUTER JOIN (b JOIN c)".
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     .. note:: The "unnested" flag does **not** affect the JOIN rendered
							 | 
						||
| 
								 | 
							
								        from a many-to-many association table, e.g. a table configured
							 | 
						||
| 
								 | 
							
								        as :paramref:`_orm.relationship.secondary`, to the target table; for
							 | 
						||
| 
								 | 
							
								        correctness of results, these joins are always INNER and are
							 | 
						||
| 
								 | 
							
								        therefore right-nested if linked to an OUTER join.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     .. versionchanged:: 1.0.0 ``innerjoin=True`` now implies
							 | 
						||
| 
								 | 
							
								        ``innerjoin="nested"``, whereas in 0.9 it implied
							 | 
						||
| 
								 | 
							
								        ``innerjoin="unnested"``.  In order to achieve the pre-1.0 "unnested"
							 | 
						||
| 
								 | 
							
								        inner join behavior, use the value ``innerjoin="unnested"``.
							 | 
						||
| 
								 | 
							
								        See :ref:`migration_3008`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. note::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The joins produced by :func:`_orm.joinedload` are **anonymously
							 | 
						||
| 
								 | 
							
								        aliased**.  The criteria by which the join proceeds cannot be
							 | 
						||
| 
								 | 
							
								        modified, nor can the :class:`_query.Query`
							 | 
						||
| 
								 | 
							
								        refer to these joins in any way,
							 | 
						||
| 
								 | 
							
								        including ordering.  See :ref:`zen_of_eager_loading` for further
							 | 
						||
| 
								 | 
							
								        detail.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        To produce a specific SQL JOIN which is explicitly available, use
							 | 
						||
| 
								 | 
							
								        :meth:`_query.Query.join`.
							 | 
						||
| 
								 | 
							
								        To combine explicit JOINs with eager loading
							 | 
						||
| 
								 | 
							
								        of collections, use :func:`_orm.contains_eager`; see
							 | 
						||
| 
								 | 
							
								        :ref:`contains_eager`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`loading_toplevel`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`joined_eager_loading`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    loader = loadopt.set_relationship_strategy(attr, {"lazy": "joined"})
							 | 
						||
| 
								 | 
							
								    if innerjoin is not None:
							 | 
						||
| 
								 | 
							
								        loader.local_opts["innerjoin"] = innerjoin
							 | 
						||
| 
								 | 
							
								    return loader
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@joinedload._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def joinedload(*keys, **kw):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(_UnboundLoad.joinedload, keys, False, kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def subqueryload(loadopt, attr):
							 | 
						||
| 
								 | 
							
								    """Indicate that the given attribute should be loaded using
							 | 
						||
| 
								 | 
							
								    subquery eager loading.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    examples::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # subquery-load the "orders" collection on "User"
							 | 
						||
| 
								 | 
							
								        query(User).options(subqueryload(User.orders))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # subquery-load Order.items and then Item.keywords
							 | 
						||
| 
								 | 
							
								        query(Order).options(
							 | 
						||
| 
								 | 
							
								            subqueryload(Order.items).subqueryload(Item.keywords))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # lazily load Order.items, but when Items are loaded,
							 | 
						||
| 
								 | 
							
								        # subquery-load the keywords collection
							 | 
						||
| 
								 | 
							
								        query(Order).options(
							 | 
						||
| 
								 | 
							
								            lazyload(Order.items).subqueryload(Item.keywords))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`loading_toplevel`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`subquery_eager_loading`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return loadopt.set_relationship_strategy(attr, {"lazy": "subquery"})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@subqueryload._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def subqueryload(*keys):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(_UnboundLoad.subqueryload, keys, False, {})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def selectinload(loadopt, attr):
							 | 
						||
| 
								 | 
							
								    """Indicate that the given attribute should be loaded using
							 | 
						||
| 
								 | 
							
								    SELECT IN eager loading.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    examples::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # selectin-load the "orders" collection on "User"
							 | 
						||
| 
								 | 
							
								        query(User).options(selectinload(User.orders))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # selectin-load Order.items and then Item.keywords
							 | 
						||
| 
								 | 
							
								        query(Order).options(
							 | 
						||
| 
								 | 
							
								            selectinload(Order.items).selectinload(Item.keywords))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # lazily load Order.items, but when Items are loaded,
							 | 
						||
| 
								 | 
							
								        # selectin-load the keywords collection
							 | 
						||
| 
								 | 
							
								        query(Order).options(
							 | 
						||
| 
								 | 
							
								            lazyload(Order.items).selectinload(Item.keywords))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionadded:: 1.2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`loading_toplevel`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`selectin_eager_loading`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return loadopt.set_relationship_strategy(attr, {"lazy": "selectin"})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@selectinload._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def selectinload(*keys):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(_UnboundLoad.selectinload, keys, False, {})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def lazyload(loadopt, attr):
							 | 
						||
| 
								 | 
							
								    """Indicate that the given attribute should be loaded using "lazy"
							 | 
						||
| 
								 | 
							
								    loading.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`loading_toplevel`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`lazy_loading`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return loadopt.set_relationship_strategy(attr, {"lazy": "select"})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@lazyload._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def lazyload(*keys):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(_UnboundLoad.lazyload, keys, False, {})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def immediateload(loadopt, attr):
							 | 
						||
| 
								 | 
							
								    """Indicate that the given attribute should be loaded using
							 | 
						||
| 
								 | 
							
								    an immediate load with a per-attribute SELECT statement.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The load is achieved using the "lazyloader" strategy and does not
							 | 
						||
| 
								 | 
							
								    fire off any additional eager loaders.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The :func:`.immediateload` option is superseded in general
							 | 
						||
| 
								 | 
							
								    by the :func:`.selectinload` option, which performs the same task
							 | 
						||
| 
								 | 
							
								    more efficiently by emitting a SELECT for all loaded objects.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`loading_toplevel`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`selectin_eager_loading`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    loader = loadopt.set_relationship_strategy(attr, {"lazy": "immediate"})
							 | 
						||
| 
								 | 
							
								    return loader
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@immediateload._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def immediateload(*keys):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(_UnboundLoad.immediateload, keys, False, {})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def noload(loadopt, attr):
							 | 
						||
| 
								 | 
							
								    """Indicate that the given relationship attribute should remain unloaded.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The relationship attribute will return ``None`` when accessed without
							 | 
						||
| 
								 | 
							
								    producing any loading effect.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :func:`_orm.noload` applies to :func:`_orm.relationship` attributes; for
							 | 
						||
| 
								 | 
							
								    column-based attributes, see :func:`_orm.defer`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. note:: Setting this loading strategy as the default strategy
							 | 
						||
| 
								 | 
							
								        for a relationship using the :paramref:`.orm.relationship.lazy`
							 | 
						||
| 
								 | 
							
								        parameter may cause issues with flushes, such if a delete operation
							 | 
						||
| 
								 | 
							
								        needs to load related objects and instead ``None`` was returned.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`loading_toplevel`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return loadopt.set_relationship_strategy(attr, {"lazy": "noload"})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@noload._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def noload(*keys):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(_UnboundLoad.noload, keys, False, {})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def raiseload(loadopt, attr, sql_only=False):
							 | 
						||
| 
								 | 
							
								    """Indicate that the given attribute should raise an error if accessed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    A relationship attribute configured with :func:`_orm.raiseload` will
							 | 
						||
| 
								 | 
							
								    raise an :exc:`~sqlalchemy.exc.InvalidRequestError` upon access.   The
							 | 
						||
| 
								 | 
							
								    typical way this is useful is when an application is attempting to ensure
							 | 
						||
| 
								 | 
							
								    that all relationship attributes that are accessed in a particular context
							 | 
						||
| 
								 | 
							
								    would have been already loaded via eager loading.  Instead of having
							 | 
						||
| 
								 | 
							
								    to read through SQL logs to ensure lazy loads aren't occurring, this
							 | 
						||
| 
								 | 
							
								    strategy will cause them to raise immediately.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :func:`_orm.raiseload` applies to :func:`_orm.relationship`
							 | 
						||
| 
								 | 
							
								    attributes only.
							 | 
						||
| 
								 | 
							
								    In order to apply raise-on-SQL behavior to a column-based attribute,
							 | 
						||
| 
								 | 
							
								    use the :paramref:`.orm.defer.raiseload` parameter on the :func:`.defer`
							 | 
						||
| 
								 | 
							
								    loader option.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param sql_only: if True, raise only if the lazy load would emit SQL, but
							 | 
						||
| 
								 | 
							
								     not if it is only checking the identity map, or determining that the
							 | 
						||
| 
								 | 
							
								     related value should just be None due to missing keys.  When False, the
							 | 
						||
| 
								 | 
							
								     strategy will raise for all varieties of relationship loading.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionadded:: 1.1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`loading_toplevel`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`prevent_lazy_with_raiseload`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`deferred_raiseload`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return loadopt.set_relationship_strategy(
							 | 
						||
| 
								 | 
							
								        attr, {"lazy": "raise_on_sql" if sql_only else "raise"}
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@raiseload._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def raiseload(*keys, **kw):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(_UnboundLoad.raiseload, keys, False, kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def defaultload(loadopt, attr):
							 | 
						||
| 
								 | 
							
								    """Indicate an attribute should load using its default loader style.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This method is used to link to other loader options further into
							 | 
						||
| 
								 | 
							
								    a chain of attributes without altering the loader style of the links
							 | 
						||
| 
								 | 
							
								    along the chain.  For example, to set joined eager loading for an
							 | 
						||
| 
								 | 
							
								    element of an element::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(
							 | 
						||
| 
								 | 
							
								            defaultload(MyClass.someattribute).
							 | 
						||
| 
								 | 
							
								            joinedload(MyOtherClass.someotherattribute)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :func:`.defaultload` is also useful for setting column-level options
							 | 
						||
| 
								 | 
							
								    on a related class, namely that of :func:`.defer` and :func:`.undefer`::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(
							 | 
						||
| 
								 | 
							
								            defaultload(MyClass.someattribute).
							 | 
						||
| 
								 | 
							
								            defer("some_column").
							 | 
						||
| 
								 | 
							
								            undefer("some_other_column")
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :meth:`_orm.Load.options` - allows for complex hierarchical
							 | 
						||
| 
								 | 
							
								        loader option structures with less verbosity than with individual
							 | 
						||
| 
								 | 
							
								        :func:`.defaultload` directives.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`relationship_loader_options`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`deferred_loading_w_multiple`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return loadopt.set_relationship_strategy(attr, None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@defaultload._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def defaultload(*keys):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(_UnboundLoad.defaultload, keys, False, {})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def defer(loadopt, key, raiseload=False):
							 | 
						||
| 
								 | 
							
								    r"""Indicate that the given column-oriented attribute should be deferred,
							 | 
						||
| 
								 | 
							
								    e.g. not loaded until accessed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    e.g.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        from sqlalchemy.orm import defer
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(
							 | 
						||
| 
								 | 
							
								                            defer("attribute_one"),
							 | 
						||
| 
								 | 
							
								                            defer("attribute_two"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(
							 | 
						||
| 
								 | 
							
								                            defer(MyClass.attribute_one),
							 | 
						||
| 
								 | 
							
								                            defer(MyClass.attribute_two))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    To specify a deferred load of an attribute on a related class,
							 | 
						||
| 
								 | 
							
								    the path can be specified one token at a time, specifying the loading
							 | 
						||
| 
								 | 
							
								    style for each link along the chain.  To leave the loading style
							 | 
						||
| 
								 | 
							
								    for a link unchanged, use :func:`_orm.defaultload`::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(defaultload("someattr").defer("some_column"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    A :class:`_orm.Load` object that is present on a certain path can have
							 | 
						||
| 
								 | 
							
								    :meth:`_orm.Load.defer` called multiple times,
							 | 
						||
| 
								 | 
							
								    each will operate on the same
							 | 
						||
| 
								 | 
							
								    parent entity::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(
							 | 
						||
| 
								 | 
							
								                        defaultload("someattr").
							 | 
						||
| 
								 | 
							
								                            defer("some_column").
							 | 
						||
| 
								 | 
							
								                            defer("some_other_column").
							 | 
						||
| 
								 | 
							
								                            defer("another_column")
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param key: Attribute to be deferred.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param raiseload: raise :class:`.InvalidRequestError` if the column
							 | 
						||
| 
								 | 
							
								     value is to be loaded from emitting SQL.   Used to prevent unwanted
							 | 
						||
| 
								 | 
							
								     SQL from being emitted.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     .. versionadded:: 1.4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`deferred_raiseload`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param \*addl_attrs: This option supports the old 0.8 style
							 | 
						||
| 
								 | 
							
								     of specifying a path as a series of attributes, which is now superseded
							 | 
						||
| 
								 | 
							
								     by the method-chained style.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. deprecated:: 0.9  The \*addl_attrs on :func:`_orm.defer` is
							 | 
						||
| 
								 | 
							
								           deprecated and will be removed in a future release.   Please
							 | 
						||
| 
								 | 
							
								           use method chaining in conjunction with defaultload() to
							 | 
						||
| 
								 | 
							
								           indicate a path.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`deferred`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :func:`_orm.undefer`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    strategy = {"deferred": True, "instrument": True}
							 | 
						||
| 
								 | 
							
								    if raiseload:
							 | 
						||
| 
								 | 
							
								        strategy["raiseload"] = True
							 | 
						||
| 
								 | 
							
								    return loadopt.set_column_strategy((key,), strategy)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@defer._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def defer(key, *addl_attrs, **kw):
							 | 
						||
| 
								 | 
							
								    if addl_attrs:
							 | 
						||
| 
								 | 
							
								        util.warn_deprecated(
							 | 
						||
| 
								 | 
							
								            "The *addl_attrs on orm.defer is deprecated.  Please use "
							 | 
						||
| 
								 | 
							
								            "method chaining in conjunction with defaultload() to "
							 | 
						||
| 
								 | 
							
								            "indicate a path.",
							 | 
						||
| 
								 | 
							
								            version="1.3",
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(
							 | 
						||
| 
								 | 
							
								        _UnboundLoad.defer, (key,) + addl_attrs, False, kw
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def undefer(loadopt, key):
							 | 
						||
| 
								 | 
							
								    r"""Indicate that the given column-oriented attribute should be undeferred,
							 | 
						||
| 
								 | 
							
								    e.g. specified within the SELECT statement of the entity as a whole.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The column being undeferred is typically set up on the mapping as a
							 | 
						||
| 
								 | 
							
								    :func:`.deferred` attribute.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is part of the :class:`_orm.Load` interface and supports
							 | 
						||
| 
								 | 
							
								    both method-chained and standalone operation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Examples::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # undefer two columns
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(undefer("col1"), undefer("col2"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # undefer all columns specific to a single class using Load + *
							 | 
						||
| 
								 | 
							
								        session.query(MyClass, MyOtherClass).options(
							 | 
						||
| 
								 | 
							
								            Load(MyClass).undefer("*"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # undefer a column on a related object
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(
							 | 
						||
| 
								 | 
							
								            defaultload(MyClass.items).undefer('text'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param key: Attribute to be undeferred.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param \*addl_attrs: This option supports the old 0.8 style
							 | 
						||
| 
								 | 
							
								     of specifying a path as a series of attributes, which is now superseded
							 | 
						||
| 
								 | 
							
								     by the method-chained style.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. deprecated:: 0.9  The \*addl_attrs on :func:`_orm.undefer` is
							 | 
						||
| 
								 | 
							
								           deprecated and will be removed in a future release.   Please
							 | 
						||
| 
								 | 
							
								           use method chaining in conjunction with defaultload() to
							 | 
						||
| 
								 | 
							
								           indicate a path.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`deferred`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :func:`_orm.defer`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :func:`_orm.undefer_group`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return loadopt.set_column_strategy(
							 | 
						||
| 
								 | 
							
								        (key,), {"deferred": False, "instrument": True}
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@undefer._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def undefer(key, *addl_attrs):
							 | 
						||
| 
								 | 
							
								    if addl_attrs:
							 | 
						||
| 
								 | 
							
								        util.warn_deprecated(
							 | 
						||
| 
								 | 
							
								            "The *addl_attrs on orm.undefer is deprecated.  Please use "
							 | 
						||
| 
								 | 
							
								            "method chaining in conjunction with defaultload() to "
							 | 
						||
| 
								 | 
							
								            "indicate a path.",
							 | 
						||
| 
								 | 
							
								            version="1.3",
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(
							 | 
						||
| 
								 | 
							
								        _UnboundLoad.undefer, (key,) + addl_attrs, False, {}
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def undefer_group(loadopt, name):
							 | 
						||
| 
								 | 
							
								    """Indicate that columns within the given deferred group name should be
							 | 
						||
| 
								 | 
							
								    undeferred.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The columns being undeferred are set up on the mapping as
							 | 
						||
| 
								 | 
							
								    :func:`.deferred` attributes and include a "group" name.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    E.g::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(undefer_group("large_attrs"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    To undefer a group of attributes on a related entity, the path can be
							 | 
						||
| 
								 | 
							
								    spelled out using relationship loader options, such as
							 | 
						||
| 
								 | 
							
								    :func:`_orm.defaultload`::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        session.query(MyClass).options(
							 | 
						||
| 
								 | 
							
								            defaultload("someattr").undefer_group("large_attrs"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionchanged:: 0.9.0 :func:`_orm.undefer_group` is now specific to a
							 | 
						||
| 
								 | 
							
								       particular entity load path.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`deferred`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :func:`_orm.defer`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :func:`_orm.undefer`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return loadopt.set_column_strategy(
							 | 
						||
| 
								 | 
							
								        "*", None, {"undefer_group_%s" % name: True}, opts_only=True
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@undefer_group._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def undefer_group(name):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad().undefer_group(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def with_expression(loadopt, key, expression):
							 | 
						||
| 
								 | 
							
								    r"""Apply an ad-hoc SQL expression to a "deferred expression" attribute.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This option is used in conjunction with the :func:`_orm.query_expression`
							 | 
						||
| 
								 | 
							
								    mapper-level construct that indicates an attribute which should be the
							 | 
						||
| 
								 | 
							
								    target of an ad-hoc SQL expression.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    E.g.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        sess.query(SomeClass).options(
							 | 
						||
| 
								 | 
							
								            with_expression(SomeClass.x_y_expr, SomeClass.x + SomeClass.y)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionadded:: 1.2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param key: Attribute to be undeferred.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param expr: SQL expression to be applied to the attribute.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. note:: the target attribute is populated only if the target object
							 | 
						||
| 
								 | 
							
								       is **not currently loaded** in the current :class:`_orm.Session`
							 | 
						||
| 
								 | 
							
								       unless the :meth:`_query.Query.populate_existing` method is used.
							 | 
						||
| 
								 | 
							
								       Please refer to :ref:`mapper_querytime_expression` for complete
							 | 
						||
| 
								 | 
							
								       usage details.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`mapper_querytime_expression`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    expression = coercions.expect(
							 | 
						||
| 
								 | 
							
								        roles.LabeledColumnExprRole, _orm_full_deannotate(expression)
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return loadopt.set_column_strategy(
							 | 
						||
| 
								 | 
							
								        (key,), {"query_expression": True}, opts={"expression": expression}
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@with_expression._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def with_expression(key, expression):
							 | 
						||
| 
								 | 
							
								    return _UnboundLoad._from_keys(
							 | 
						||
| 
								 | 
							
								        _UnboundLoad.with_expression, (key,), False, {"expression": expression}
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@loader_option()
							 | 
						||
| 
								 | 
							
								def selectin_polymorphic(loadopt, classes):
							 | 
						||
| 
								 | 
							
								    """Indicate an eager load should take place for all attributes
							 | 
						||
| 
								 | 
							
								    specific to a subclass.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This uses an additional SELECT with IN against all matched primary
							 | 
						||
| 
								 | 
							
								    key values, and is the per-query analogue to the ``"selectin"``
							 | 
						||
| 
								 | 
							
								    setting on the :paramref:`.mapper.polymorphic_load` parameter.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionadded:: 1.2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`polymorphic_selectin`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    loadopt.set_class_strategy(
							 | 
						||
| 
								 | 
							
								        {"selectinload_polymorphic": True},
							 | 
						||
| 
								 | 
							
								        opts={
							 | 
						||
| 
								 | 
							
								            "entities": tuple(
							 | 
						||
| 
								 | 
							
								                sorted((inspect(cls) for cls in classes), key=id)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    return loadopt
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@selectin_polymorphic._add_unbound_fn
							 | 
						||
| 
								 | 
							
								def selectin_polymorphic(base_cls, classes):
							 | 
						||
| 
								 | 
							
								    ul = _UnboundLoad()
							 | 
						||
| 
								 | 
							
								    ul.is_class_strategy = True
							 | 
						||
| 
								 | 
							
								    ul.path = (inspect(base_cls),)
							 | 
						||
| 
								 | 
							
								    ul.selectin_polymorphic(classes)
							 | 
						||
| 
								 | 
							
								    return ul
							 |