1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018 |
- # orm/interfaces.py
- # Copyright (C) 2005-2024 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
- """
- Contains various base classes used throughout the ORM.
- Defines some key base classes prominent within the internals.
- This module and the classes within are mostly private, though some attributes
- are exposed when inspecting mappings.
- """
- from __future__ import absolute_import
- import collections
- from . import exc as orm_exc
- from . import path_registry
- from .base import _MappedAttribute # noqa
- from .base import EXT_CONTINUE
- from .base import EXT_SKIP
- from .base import EXT_STOP
- from .base import InspectionAttr # noqa
- from .base import InspectionAttrInfo # noqa
- from .base import MANYTOMANY
- from .base import MANYTOONE
- from .base import NOT_EXTENSION
- from .base import ONETOMANY
- from .. import inspect
- from .. import inspection
- from .. import util
- from ..sql import operators
- from ..sql import roles
- from ..sql import visitors
- from ..sql.base import ExecutableOption
- from ..sql.traversals import HasCacheKey
- __all__ = (
- "EXT_CONTINUE",
- "EXT_STOP",
- "EXT_SKIP",
- "ONETOMANY",
- "MANYTOMANY",
- "MANYTOONE",
- "NOT_EXTENSION",
- "LoaderStrategy",
- "MapperOption",
- "LoaderOption",
- "MapperProperty",
- "PropComparator",
- "StrategizedProperty",
- )
- class ORMStatementRole(roles.StatementRole):
- _role_name = (
- "Executable SQL or text() construct, including ORM " "aware objects"
- )
- class ORMColumnsClauseRole(roles.ColumnsClauseRole):
- _role_name = "ORM mapped entity, aliased entity, or Column expression"
- class ORMEntityColumnsClauseRole(ORMColumnsClauseRole):
- _role_name = "ORM mapped or aliased entity"
- class ORMFromClauseRole(roles.StrictFromClauseRole):
- _role_name = "ORM mapped entity, aliased entity, or FROM expression"
- @inspection._self_inspects
- class MapperProperty(
- HasCacheKey, _MappedAttribute, InspectionAttr, util.MemoizedSlots
- ):
- """Represent a particular class attribute mapped by :class:`_orm.Mapper`.
- The most common occurrences of :class:`.MapperProperty` are the
- mapped :class:`_schema.Column`, which is represented in a mapping as
- an instance of :class:`.ColumnProperty`,
- and a reference to another class produced by :func:`_orm.relationship`,
- represented in the mapping as an instance of
- :class:`.RelationshipProperty`.
- """
- __slots__ = (
- "_configure_started",
- "_configure_finished",
- "parent",
- "key",
- "info",
- )
- _cache_key_traversal = [
- ("parent", visitors.ExtendedInternalTraversal.dp_has_cache_key),
- ("key", visitors.ExtendedInternalTraversal.dp_string),
- ]
- cascade = frozenset()
- """The set of 'cascade' attribute names.
- This collection is checked before the 'cascade_iterator' method is called.
- The collection typically only applies to a RelationshipProperty.
- """
- is_property = True
- """Part of the InspectionAttr interface; states this object is a
- mapper property.
- """
- @property
- def _links_to_entity(self):
- """True if this MapperProperty refers to a mapped entity.
- Should only be True for RelationshipProperty, False for all others.
- """
- raise NotImplementedError()
- def _memoized_attr_info(self):
- """Info dictionary associated with the object, allowing user-defined
- data to be associated with this :class:`.InspectionAttr`.
- The dictionary is generated when first accessed. Alternatively,
- it can be specified as a constructor argument to the
- :func:`.column_property`, :func:`_orm.relationship`, or
- :func:`.composite`
- functions.
- .. versionchanged:: 1.0.0 :attr:`.MapperProperty.info` is also
- available on extension types via the
- :attr:`.InspectionAttrInfo.info` attribute, so that it can apply
- to a wider variety of ORM and extension constructs.
- .. seealso::
- :attr:`.QueryableAttribute.info`
- :attr:`.SchemaItem.info`
- """
- return {}
- def setup(self, context, query_entity, path, adapter, **kwargs):
- """Called by Query for the purposes of constructing a SQL statement.
- Each MapperProperty associated with the target mapper processes the
- statement referenced by the query context, adding columns and/or
- criterion as appropriate.
- """
- def create_row_processor(
- self, context, query_entity, path, mapper, result, adapter, populators
- ):
- """Produce row processing functions and append to the given
- set of populators lists.
- """
- def cascade_iterator(
- self, type_, state, dict_, visited_states, halt_on=None
- ):
- """Iterate through instances related to the given instance for
- a particular 'cascade', starting with this MapperProperty.
- Return an iterator3-tuples (instance, mapper, state).
- Note that the 'cascade' collection on this MapperProperty is
- checked first for the given type before cascade_iterator is called.
- This method typically only applies to RelationshipProperty.
- """
- return iter(())
- def set_parent(self, parent, init):
- """Set the parent mapper that references this MapperProperty.
- This method is overridden by some subclasses to perform extra
- setup when the mapper is first known.
- """
- self.parent = parent
- def instrument_class(self, mapper):
- """Hook called by the Mapper to the property to initiate
- instrumentation of the class attribute managed by this
- MapperProperty.
- The MapperProperty here will typically call out to the
- attributes module to set up an InstrumentedAttribute.
- This step is the first of two steps to set up an InstrumentedAttribute,
- and is called early in the mapper setup process.
- The second step is typically the init_class_attribute step,
- called from StrategizedProperty via the post_instrument_class()
- hook. This step assigns additional state to the InstrumentedAttribute
- (specifically the "impl") which has been determined after the
- MapperProperty has determined what kind of persistence
- management it needs to do (e.g. scalar, object, collection, etc).
- """
- def __init__(self):
- self._configure_started = False
- self._configure_finished = False
- def init(self):
- """Called after all mappers are created to assemble
- relationships between mappers and perform other post-mapper-creation
- initialization steps.
- """
- self._configure_started = True
- self.do_init()
- self._configure_finished = True
- @property
- def class_attribute(self):
- """Return the class-bound descriptor corresponding to this
- :class:`.MapperProperty`.
- This is basically a ``getattr()`` call::
- return getattr(self.parent.class_, self.key)
- I.e. if this :class:`.MapperProperty` were named ``addresses``,
- and the class to which it is mapped is ``User``, this sequence
- is possible::
- >>> from sqlalchemy import inspect
- >>> mapper = inspect(User)
- >>> addresses_property = mapper.attrs.addresses
- >>> addresses_property.class_attribute is User.addresses
- True
- >>> User.addresses.property is addresses_property
- True
- """
- return getattr(self.parent.class_, self.key)
- def do_init(self):
- """Perform subclass-specific initialization post-mapper-creation
- steps.
- This is a template method called by the ``MapperProperty``
- object's init() method.
- """
- def post_instrument_class(self, mapper):
- """Perform instrumentation adjustments that need to occur
- after init() has completed.
- The given Mapper is the Mapper invoking the operation, which
- may not be the same Mapper as self.parent in an inheritance
- scenario; however, Mapper will always at least be a sub-mapper of
- self.parent.
- This method is typically used by StrategizedProperty, which delegates
- it to LoaderStrategy.init_class_attribute() to perform final setup
- on the class-bound InstrumentedAttribute.
- """
- def merge(
- self,
- session,
- source_state,
- source_dict,
- dest_state,
- dest_dict,
- load,
- _recursive,
- _resolve_conflict_map,
- ):
- """Merge the attribute represented by this ``MapperProperty``
- from source to destination object.
- """
- def __repr__(self):
- return "<%s at 0x%x; %s>" % (
- self.__class__.__name__,
- id(self),
- getattr(self, "key", "no key"),
- )
- @inspection._self_inspects
- class PropComparator(operators.ColumnOperators):
- r"""Defines SQL operators for :class:`.MapperProperty` objects.
- SQLAlchemy allows for operators to
- be redefined at both the Core and ORM level. :class:`.PropComparator`
- is the base class of operator redefinition for ORM-level operations,
- including those of :class:`.ColumnProperty`,
- :class:`.RelationshipProperty`, and :class:`.CompositeProperty`.
- .. note:: With the advent of Hybrid properties introduced in SQLAlchemy
- 0.7, as well as Core-level operator redefinition in
- SQLAlchemy 0.8, the use case for user-defined :class:`.PropComparator`
- instances is extremely rare. See :ref:`hybrids_toplevel` as well
- as :ref:`types_operators`.
- User-defined subclasses of :class:`.PropComparator` may be created. The
- built-in Python comparison and math operator methods, such as
- :meth:`.operators.ColumnOperators.__eq__`,
- :meth:`.operators.ColumnOperators.__lt__`, and
- :meth:`.operators.ColumnOperators.__add__`, can be overridden to provide
- new operator behavior. The custom :class:`.PropComparator` is passed to
- the :class:`.MapperProperty` instance via the ``comparator_factory``
- argument. In each case,
- the appropriate subclass of :class:`.PropComparator` should be used::
- # definition of custom PropComparator subclasses
- from sqlalchemy.orm.properties import \
- ColumnProperty,\
- CompositeProperty,\
- RelationshipProperty
- class MyColumnComparator(ColumnProperty.Comparator):
- def __eq__(self, other):
- return self.__clause_element__() == other
- class MyRelationshipComparator(RelationshipProperty.Comparator):
- def any(self, expression):
- "define the 'any' operation"
- # ...
- class MyCompositeComparator(CompositeProperty.Comparator):
- def __gt__(self, other):
- "redefine the 'greater than' operation"
- return sql.and_(*[a>b for a, b in
- zip(self.__clause_element__().clauses,
- other.__composite_values__())])
- # application of custom PropComparator subclasses
- from sqlalchemy.orm import column_property, relationship, composite
- from sqlalchemy import Column, String
- class SomeMappedClass(Base):
- some_column = column_property(Column("some_column", String),
- comparator_factory=MyColumnComparator)
- some_relationship = relationship(SomeOtherClass,
- comparator_factory=MyRelationshipComparator)
- some_composite = composite(
- Column("a", String), Column("b", String),
- comparator_factory=MyCompositeComparator
- )
- Note that for column-level operator redefinition, it's usually
- simpler to define the operators at the Core level, using the
- :attr:`.TypeEngine.comparator_factory` attribute. See
- :ref:`types_operators` for more detail.
- .. seealso::
- :class:`.ColumnProperty.Comparator`
- :class:`.RelationshipProperty.Comparator`
- :class:`.CompositeProperty.Comparator`
- :class:`.ColumnOperators`
- :ref:`types_operators`
- :attr:`.TypeEngine.comparator_factory`
- """
- __slots__ = "prop", "property", "_parententity", "_adapt_to_entity"
- __visit_name__ = "orm_prop_comparator"
- def __init__(
- self,
- prop,
- parentmapper,
- adapt_to_entity=None,
- ):
- self.prop = self.property = prop
- self._parententity = adapt_to_entity or parentmapper
- self._adapt_to_entity = adapt_to_entity
- def __clause_element__(self):
- raise NotImplementedError("%r" % self)
- def _bulk_update_tuples(self, value):
- """Receive a SQL expression that represents a value in the SET
- clause of an UPDATE statement.
- Return a tuple that can be passed to a :class:`_expression.Update`
- construct.
- """
- return [(self.__clause_element__(), value)]
- def adapt_to_entity(self, adapt_to_entity):
- """Return a copy of this PropComparator which will use the given
- :class:`.AliasedInsp` to produce corresponding expressions.
- """
- return self.__class__(self.prop, self._parententity, adapt_to_entity)
- @property
- def _parentmapper(self):
- """legacy; this is renamed to _parententity to be
- compatible with QueryableAttribute."""
- return inspect(self._parententity).mapper
- @property
- def _propagate_attrs(self):
- # this suits the case in coercions where we don't actually
- # call ``__clause_element__()`` but still need to get
- # resolved._propagate_attrs. See #6558.
- return util.immutabledict(
- {
- "compile_state_plugin": "orm",
- "plugin_subject": self._parentmapper,
- }
- )
- @property
- def adapter(self):
- """Produce a callable that adapts column expressions
- to suit an aliased version of this comparator.
- """
- if self._adapt_to_entity is None:
- return None
- else:
- return self._adapt_to_entity._adapt_element
- @property
- def info(self):
- return self.property.info
- @staticmethod
- def any_op(a, b, **kwargs):
- return a.any(b, **kwargs)
- @staticmethod
- def has_op(a, b, **kwargs):
- return a.has(b, **kwargs)
- @staticmethod
- def of_type_op(a, class_):
- return a.of_type(class_)
- def of_type(self, class_):
- r"""Redefine this object in terms of a polymorphic subclass,
- :func:`_orm.with_polymorphic` construct, or :func:`_orm.aliased`
- construct.
- Returns a new PropComparator from which further criterion can be
- evaluated.
- e.g.::
- query.join(Company.employees.of_type(Engineer)).\
- filter(Engineer.name=='foo')
- :param \class_: a class or mapper indicating that criterion will be
- against this specific subclass.
- .. seealso::
- :ref:`queryguide_join_onclause` - in the :ref:`queryguide_toplevel`
- :ref:`inheritance_of_type`
- """
- return self.operate(PropComparator.of_type_op, class_)
- def and_(self, *criteria):
- """Add additional criteria to the ON clause that's represented by this
- relationship attribute.
- E.g.::
- stmt = select(User).join(
- User.addresses.and_(Address.email_address != 'foo')
- )
- stmt = select(User).options(
- joinedload(User.addresses.and_(Address.email_address != 'foo'))
- )
- .. versionadded:: 1.4
- .. seealso::
- :ref:`orm_queryguide_join_on_augmented`
- :ref:`loader_option_criteria`
- :func:`.with_loader_criteria`
- """
- return self.operate(operators.and_, *criteria)
- def any(self, criterion=None, **kwargs):
- r"""Return true if this collection contains any member that meets the
- given criterion.
- The usual implementation of ``any()`` is
- :meth:`.RelationshipProperty.Comparator.any`.
- :param criterion: an optional ClauseElement formulated against the
- member class' table or attributes.
- :param \**kwargs: key/value pairs corresponding to member class
- attribute names which will be compared via equality to the
- corresponding values.
- """
- return self.operate(PropComparator.any_op, criterion, **kwargs)
- def has(self, criterion=None, **kwargs):
- r"""Return true if this element references a member which meets the
- given criterion.
- The usual implementation of ``has()`` is
- :meth:`.RelationshipProperty.Comparator.has`.
- :param criterion: an optional ClauseElement formulated against the
- member class' table or attributes.
- :param \**kwargs: key/value pairs corresponding to member class
- attribute names which will be compared via equality to the
- corresponding values.
- """
- return self.operate(PropComparator.has_op, criterion, **kwargs)
- class StrategizedProperty(MapperProperty):
- """A MapperProperty which uses selectable strategies to affect
- loading behavior.
- There is a single strategy selected by default. Alternate
- strategies can be selected at Query time through the usage of
- ``StrategizedOption`` objects via the Query.options() method.
- The mechanics of StrategizedProperty are used for every Query
- invocation for every mapped attribute participating in that Query,
- to determine first how the attribute will be rendered in SQL
- and secondly how the attribute will retrieve a value from a result
- row and apply it to a mapped object. The routines here are very
- performance-critical.
- """
- __slots__ = (
- "_strategies",
- "strategy",
- "_wildcard_token",
- "_default_path_loader_key",
- )
- inherit_cache = True
- strategy_wildcard_key = None
- def _memoized_attr__wildcard_token(self):
- return (
- "%s:%s"
- % (self.strategy_wildcard_key, path_registry._WILDCARD_TOKEN),
- )
- def _memoized_attr__default_path_loader_key(self):
- return (
- "loader",
- (
- "%s:%s"
- % (self.strategy_wildcard_key, path_registry._DEFAULT_TOKEN),
- ),
- )
- def _get_context_loader(self, context, path):
- load = None
- search_path = path[self]
- # search among: exact match, "attr.*", "default" strategy
- # if any.
- for path_key in (
- search_path._loader_key,
- search_path._wildcard_path_loader_key,
- search_path._default_path_loader_key,
- ):
- if path_key in context.attributes:
- load = context.attributes[path_key]
- break
- return load
- def _get_strategy(self, key):
- try:
- return self._strategies[key]
- except KeyError:
- pass
- # run outside to prevent transfer of exception context
- cls = self._strategy_lookup(self, *key)
- # this previously was setting self._strategies[cls], that's
- # a bad idea; should use strategy key at all times because every
- # strategy has multiple keys at this point
- self._strategies[key] = strategy = cls(self, key)
- return strategy
- def setup(self, context, query_entity, path, adapter, **kwargs):
- loader = self._get_context_loader(context, path)
- if loader and loader.strategy:
- strat = self._get_strategy(loader.strategy)
- else:
- strat = self.strategy
- strat.setup_query(
- context, query_entity, path, loader, adapter, **kwargs
- )
- def create_row_processor(
- self, context, query_entity, path, mapper, result, adapter, populators
- ):
- loader = self._get_context_loader(context, path)
- if loader and loader.strategy:
- strat = self._get_strategy(loader.strategy)
- else:
- strat = self.strategy
- strat.create_row_processor(
- context,
- query_entity,
- path,
- loader,
- mapper,
- result,
- adapter,
- populators,
- )
- def do_init(self):
- self._strategies = {}
- self.strategy = self._get_strategy(self.strategy_key)
- def post_instrument_class(self, mapper):
- if (
- not self.parent.non_primary
- and not mapper.class_manager._attr_has_impl(self.key)
- ):
- self.strategy.init_class_attribute(mapper)
- _all_strategies = collections.defaultdict(dict)
- @classmethod
- def strategy_for(cls, **kw):
- def decorate(dec_cls):
- # ensure each subclass of the strategy has its
- # own _strategy_keys collection
- if "_strategy_keys" not in dec_cls.__dict__:
- dec_cls._strategy_keys = []
- key = tuple(sorted(kw.items()))
- cls._all_strategies[cls][key] = dec_cls
- dec_cls._strategy_keys.append(key)
- return dec_cls
- return decorate
- @classmethod
- def _strategy_lookup(cls, requesting_property, *key):
- requesting_property.parent._with_polymorphic_mappers
- for prop_cls in cls.__mro__:
- if prop_cls in cls._all_strategies:
- strategies = cls._all_strategies[prop_cls]
- try:
- return strategies[key]
- except KeyError:
- pass
- for property_type, strats in cls._all_strategies.items():
- if key in strats:
- intended_property_type = property_type
- actual_strategy = strats[key]
- break
- else:
- intended_property_type = None
- actual_strategy = None
- raise orm_exc.LoaderStrategyException(
- cls,
- requesting_property,
- intended_property_type,
- actual_strategy,
- key,
- )
- class ORMOption(ExecutableOption):
- """Base class for option objects that are passed to ORM queries.
- These options may be consumed by :meth:`.Query.options`,
- :meth:`.Select.options`, or in a more general sense by any
- :meth:`.Executable.options` method. They are interpreted at
- statement compile time or execution time in modern use. The
- deprecated :class:`.MapperOption` is consumed at ORM query construction
- time.
- .. versionadded:: 1.4
- """
- __slots__ = ()
- _is_legacy_option = False
- propagate_to_loaders = False
- """if True, indicate this option should be carried along
- to "secondary" SELECT statements that occur for relationship
- lazy loaders as well as attribute load / refresh operations.
- """
- _is_compile_state = False
- _is_criteria_option = False
- _is_strategy_option = False
- def _adapt_cached_option_to_uncached_option(self, context, uncached_opt):
- """given "self" which is an option from a cached query, as well as the
- corresponding option from the uncached version of the same query,
- return the option we should use in a new query, in the context of a
- loader strategy being asked to load related rows on behalf of that
- cached query, which is assumed to be building a new query based on
- entities passed to us from the cached query.
- Currently this routine chooses between "self" and "uncached" without
- manufacturing anything new. If the option is itself a loader strategy
- option which has a path, that path needs to match to the entities being
- passed to us by the cached query, so the :class:`_orm.Load` subclass
- overrides this to return "self". For all other options, we return the
- uncached form which may have changing state, such as a
- with_loader_criteria() option which will very often have new state.
- This routine could in the future involve
- generating a new option based on both inputs if use cases arise,
- such as if with_loader_criteria() needed to match up to
- ``AliasedClass`` instances given in the parent query.
- However, longer term it might be better to restructure things such that
- ``AliasedClass`` entities are always matched up on their cache key,
- instead of identity, in things like paths and such, so that this whole
- issue of "the uncached option does not match the entities" goes away.
- However this would make ``PathRegistry`` more complicated and difficult
- to debug as well as potentially less performant in that it would be
- hashing enormous cache keys rather than a simple AliasedInsp. UNLESS,
- we could get cache keys overall to be reliably hashed into something
- like an md5 key.
- .. versionadded:: 1.4.41
- """
- if uncached_opt is not None:
- return uncached_opt
- else:
- return self
- class CompileStateOption(HasCacheKey, ORMOption):
- """base for :class:`.ORMOption` classes that affect the compilation of
- a SQL query and therefore need to be part of the cache key.
- .. note:: :class:`.CompileStateOption` is generally non-public and
- should not be used as a base class for user-defined options; instead,
- use :class:`.UserDefinedOption`, which is easier to use as it does not
- interact with ORM compilation internals or caching.
- :class:`.CompileStateOption` defines an internal attribute
- ``_is_compile_state=True`` which has the effect of the ORM compilation
- routines for SELECT and other statements will call upon these options when
- a SQL string is being compiled. As such, these classes implement
- :class:`.HasCacheKey` and need to provide robust ``_cache_key_traversal``
- structures.
- The :class:`.CompileStateOption` class is used to implement the ORM
- :class:`.LoaderOption` and :class:`.CriteriaOption` classes.
- .. versionadded:: 1.4.28
- """
- _is_compile_state = True
- def process_compile_state(self, compile_state):
- """Apply a modification to a given :class:`.CompileState`."""
- def process_compile_state_replaced_entities(
- self, compile_state, mapper_entities
- ):
- """Apply a modification to a given :class:`.CompileState`,
- given entities that were replaced by with_only_columns() or
- with_entities().
- .. versionadded:: 1.4.19
- """
- class LoaderOption(CompileStateOption):
- """Describe a loader modification to an ORM statement at compilation time.
- .. versionadded:: 1.4
- """
- def process_compile_state_replaced_entities(
- self, compile_state, mapper_entities
- ):
- """Apply a modification to a given :class:`.CompileState`,
- given entities that were replaced by with_only_columns() or
- with_entities().
- .. versionadded:: 1.4.19
- """
- self.process_compile_state(compile_state)
- def process_compile_state(self, compile_state):
- """Apply a modification to a given :class:`.CompileState`."""
- class CriteriaOption(CompileStateOption):
- """Describe a WHERE criteria modification to an ORM statement at
- compilation time.
- .. versionadded:: 1.4
- """
- _is_criteria_option = True
- def process_compile_state(self, compile_state):
- """Apply a modification to a given :class:`.CompileState`."""
- def get_global_criteria(self, attributes):
- """update additional entity criteria options in the given
- attributes dictionary.
- """
- class UserDefinedOption(ORMOption):
- """Base class for a user-defined option that can be consumed from the
- :meth:`.SessionEvents.do_orm_execute` event hook.
- """
- _is_legacy_option = False
- propagate_to_loaders = False
- """if True, indicate this option should be carried along
- to "secondary" Query objects produced during lazy loads
- or refresh operations.
- """
- def __init__(self, payload=None):
- self.payload = payload
- @util.deprecated_cls(
- "1.4",
- "The :class:`.MapperOption class is deprecated and will be removed "
- "in a future release. For "
- "modifications to queries on a per-execution basis, use the "
- ":class:`.UserDefinedOption` class to establish state within a "
- ":class:`.Query` or other Core statement, then use the "
- ":meth:`.SessionEvents.before_orm_execute` hook to consume them.",
- constructor=None,
- )
- class MapperOption(ORMOption):
- """Describe a modification to a Query"""
- _is_legacy_option = True
- propagate_to_loaders = False
- """if True, indicate this option should be carried along
- to "secondary" Query objects produced during lazy loads
- or refresh operations.
- """
- def process_query(self, query):
- """Apply a modification to the given :class:`_query.Query`."""
- def process_query_conditionally(self, query):
- """same as process_query(), except that this option may not
- apply to the given query.
- This is typically applied during a lazy load or scalar refresh
- operation to propagate options stated in the original Query to the
- new Query being used for the load. It occurs for those options that
- specify propagate_to_loaders=True.
- """
- self.process_query(query)
- class LoaderStrategy(object):
- """Describe the loading behavior of a StrategizedProperty object.
- The ``LoaderStrategy`` interacts with the querying process in three
- ways:
- * it controls the configuration of the ``InstrumentedAttribute``
- placed on a class to handle the behavior of the attribute. this
- may involve setting up class-level callable functions to fire
- off a select operation when the attribute is first accessed
- (i.e. a lazy load)
- * it processes the ``QueryContext`` at statement construction time,
- where it can modify the SQL statement that is being produced.
- For example, simple column attributes will add their represented
- column to the list of selected columns, a joined eager loader
- may establish join clauses to add to the statement.
- * It produces "row processor" functions at result fetching time.
- These "row processor" functions populate a particular attribute
- on a particular mapped instance.
- """
- __slots__ = (
- "parent_property",
- "is_class_level",
- "parent",
- "key",
- "strategy_key",
- "strategy_opts",
- )
- def __init__(self, parent, strategy_key):
- self.parent_property = parent
- self.is_class_level = False
- self.parent = self.parent_property.parent
- self.key = self.parent_property.key
- self.strategy_key = strategy_key
- self.strategy_opts = dict(strategy_key)
- def init_class_attribute(self, mapper):
- pass
- def setup_query(
- self, compile_state, query_entity, path, loadopt, adapter, **kwargs
- ):
- """Establish column and other state for a given QueryContext.
- This method fulfills the contract specified by MapperProperty.setup().
- StrategizedProperty delegates its setup() method
- directly to this method.
- """
- def create_row_processor(
- self,
- context,
- query_entity,
- path,
- loadopt,
- mapper,
- result,
- adapter,
- populators,
- ):
- """Establish row processing functions for a given QueryContext.
- This method fulfills the contract specified by
- MapperProperty.create_row_processor().
- StrategizedProperty delegates its create_row_processor() method
- directly to this method.
- """
- def __str__(self):
- return str(self.parent_property)
|