Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/interfaces.py: 64%
231 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1# orm/interfaces.py
2# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
8"""
10Contains various base classes used throughout the ORM.
12Defines some key base classes prominent within the internals.
14This module and the classes within are mostly private, though some attributes
15are exposed when inspecting mappings.
17"""
19from __future__ import absolute_import
21import collections
23from . import exc as orm_exc
24from . import path_registry
25from .base import _MappedAttribute # noqa
26from .base import EXT_CONTINUE
27from .base import EXT_SKIP
28from .base import EXT_STOP
29from .base import InspectionAttr # noqa
30from .base import InspectionAttrInfo # noqa
31from .base import MANYTOMANY
32from .base import MANYTOONE
33from .base import NOT_EXTENSION
34from .base import ONETOMANY
35from .. import inspect
36from .. import inspection
37from .. import util
38from ..sql import operators
39from ..sql import roles
40from ..sql import visitors
41from ..sql.base import ExecutableOption
42from ..sql.traversals import HasCacheKey
45__all__ = (
46 "EXT_CONTINUE",
47 "EXT_STOP",
48 "EXT_SKIP",
49 "ONETOMANY",
50 "MANYTOMANY",
51 "MANYTOONE",
52 "NOT_EXTENSION",
53 "LoaderStrategy",
54 "MapperOption",
55 "LoaderOption",
56 "MapperProperty",
57 "PropComparator",
58 "StrategizedProperty",
59)
62class ORMStatementRole(roles.StatementRole):
63 _role_name = (
64 "Executable SQL or text() construct, including ORM " "aware objects"
65 )
68class ORMColumnsClauseRole(roles.ColumnsClauseRole):
69 _role_name = "ORM mapped entity, aliased entity, or Column expression"
72class ORMEntityColumnsClauseRole(ORMColumnsClauseRole):
73 _role_name = "ORM mapped or aliased entity"
76class ORMFromClauseRole(roles.StrictFromClauseRole):
77 _role_name = "ORM mapped entity, aliased entity, or FROM expression"
80@inspection._self_inspects
81class MapperProperty(
82 HasCacheKey, _MappedAttribute, InspectionAttr, util.MemoizedSlots
83):
84 """Represent a particular class attribute mapped by :class:`_orm.Mapper`.
86 The most common occurrences of :class:`.MapperProperty` are the
87 mapped :class:`_schema.Column`, which is represented in a mapping as
88 an instance of :class:`.ColumnProperty`,
89 and a reference to another class produced by :func:`_orm.relationship`,
90 represented in the mapping as an instance of
91 :class:`.RelationshipProperty`.
93 """
95 __slots__ = (
96 "_configure_started",
97 "_configure_finished",
98 "parent",
99 "key",
100 "info",
101 )
103 _cache_key_traversal = [
104 ("parent", visitors.ExtendedInternalTraversal.dp_has_cache_key),
105 ("key", visitors.ExtendedInternalTraversal.dp_string),
106 ]
108 cascade = frozenset()
109 """The set of 'cascade' attribute names.
111 This collection is checked before the 'cascade_iterator' method is called.
113 The collection typically only applies to a RelationshipProperty.
115 """
117 is_property = True
118 """Part of the InspectionAttr interface; states this object is a
119 mapper property.
121 """
123 @property
124 def _links_to_entity(self):
125 """True if this MapperProperty refers to a mapped entity.
127 Should only be True for RelationshipProperty, False for all others.
129 """
130 raise NotImplementedError()
132 def _memoized_attr_info(self):
133 """Info dictionary associated with the object, allowing user-defined
134 data to be associated with this :class:`.InspectionAttr`.
136 The dictionary is generated when first accessed. Alternatively,
137 it can be specified as a constructor argument to the
138 :func:`.column_property`, :func:`_orm.relationship`, or
139 :func:`.composite`
140 functions.
142 .. versionchanged:: 1.0.0 :attr:`.MapperProperty.info` is also
143 available on extension types via the
144 :attr:`.InspectionAttrInfo.info` attribute, so that it can apply
145 to a wider variety of ORM and extension constructs.
147 .. seealso::
149 :attr:`.QueryableAttribute.info`
151 :attr:`.SchemaItem.info`
153 """
154 return {}
156 def setup(self, context, query_entity, path, adapter, **kwargs):
157 """Called by Query for the purposes of constructing a SQL statement.
159 Each MapperProperty associated with the target mapper processes the
160 statement referenced by the query context, adding columns and/or
161 criterion as appropriate.
163 """
165 def create_row_processor(
166 self, context, query_entity, path, mapper, result, adapter, populators
167 ):
168 """Produce row processing functions and append to the given
169 set of populators lists.
171 """
173 def cascade_iterator(
174 self, type_, state, dict_, visited_states, halt_on=None
175 ):
176 """Iterate through instances related to the given instance for
177 a particular 'cascade', starting with this MapperProperty.
179 Return an iterator3-tuples (instance, mapper, state).
181 Note that the 'cascade' collection on this MapperProperty is
182 checked first for the given type before cascade_iterator is called.
184 This method typically only applies to RelationshipProperty.
186 """
188 return iter(())
190 def set_parent(self, parent, init):
191 """Set the parent mapper that references this MapperProperty.
193 This method is overridden by some subclasses to perform extra
194 setup when the mapper is first known.
196 """
197 self.parent = parent
199 def instrument_class(self, mapper):
200 """Hook called by the Mapper to the property to initiate
201 instrumentation of the class attribute managed by this
202 MapperProperty.
204 The MapperProperty here will typically call out to the
205 attributes module to set up an InstrumentedAttribute.
207 This step is the first of two steps to set up an InstrumentedAttribute,
208 and is called early in the mapper setup process.
210 The second step is typically the init_class_attribute step,
211 called from StrategizedProperty via the post_instrument_class()
212 hook. This step assigns additional state to the InstrumentedAttribute
213 (specifically the "impl") which has been determined after the
214 MapperProperty has determined what kind of persistence
215 management it needs to do (e.g. scalar, object, collection, etc).
217 """
219 def __init__(self):
220 self._configure_started = False
221 self._configure_finished = False
223 def init(self):
224 """Called after all mappers are created to assemble
225 relationships between mappers and perform other post-mapper-creation
226 initialization steps.
229 """
230 self._configure_started = True
231 self.do_init()
232 self._configure_finished = True
234 @property
235 def class_attribute(self):
236 """Return the class-bound descriptor corresponding to this
237 :class:`.MapperProperty`.
239 This is basically a ``getattr()`` call::
241 return getattr(self.parent.class_, self.key)
243 I.e. if this :class:`.MapperProperty` were named ``addresses``,
244 and the class to which it is mapped is ``User``, this sequence
245 is possible::
247 >>> from sqlalchemy import inspect
248 >>> mapper = inspect(User)
249 >>> addresses_property = mapper.attrs.addresses
250 >>> addresses_property.class_attribute is User.addresses
251 True
252 >>> User.addresses.property is addresses_property
253 True
256 """
258 return getattr(self.parent.class_, self.key)
260 def do_init(self):
261 """Perform subclass-specific initialization post-mapper-creation
262 steps.
264 This is a template method called by the ``MapperProperty``
265 object's init() method.
267 """
269 def post_instrument_class(self, mapper):
270 """Perform instrumentation adjustments that need to occur
271 after init() has completed.
273 The given Mapper is the Mapper invoking the operation, which
274 may not be the same Mapper as self.parent in an inheritance
275 scenario; however, Mapper will always at least be a sub-mapper of
276 self.parent.
278 This method is typically used by StrategizedProperty, which delegates
279 it to LoaderStrategy.init_class_attribute() to perform final setup
280 on the class-bound InstrumentedAttribute.
282 """
284 def merge(
285 self,
286 session,
287 source_state,
288 source_dict,
289 dest_state,
290 dest_dict,
291 load,
292 _recursive,
293 _resolve_conflict_map,
294 ):
295 """Merge the attribute represented by this ``MapperProperty``
296 from source to destination object.
298 """
300 def __repr__(self):
301 return "<%s at 0x%x; %s>" % (
302 self.__class__.__name__,
303 id(self),
304 getattr(self, "key", "no key"),
305 )
308@inspection._self_inspects
309class PropComparator(operators.ColumnOperators):
310 r"""Defines SQL operators for :class:`.MapperProperty` objects.
312 SQLAlchemy allows for operators to
313 be redefined at both the Core and ORM level. :class:`.PropComparator`
314 is the base class of operator redefinition for ORM-level operations,
315 including those of :class:`.ColumnProperty`,
316 :class:`.RelationshipProperty`, and :class:`.CompositeProperty`.
318 .. note:: With the advent of Hybrid properties introduced in SQLAlchemy
319 0.7, as well as Core-level operator redefinition in
320 SQLAlchemy 0.8, the use case for user-defined :class:`.PropComparator`
321 instances is extremely rare. See :ref:`hybrids_toplevel` as well
322 as :ref:`types_operators`.
324 User-defined subclasses of :class:`.PropComparator` may be created. The
325 built-in Python comparison and math operator methods, such as
326 :meth:`.operators.ColumnOperators.__eq__`,
327 :meth:`.operators.ColumnOperators.__lt__`, and
328 :meth:`.operators.ColumnOperators.__add__`, can be overridden to provide
329 new operator behavior. The custom :class:`.PropComparator` is passed to
330 the :class:`.MapperProperty` instance via the ``comparator_factory``
331 argument. In each case,
332 the appropriate subclass of :class:`.PropComparator` should be used::
334 # definition of custom PropComparator subclasses
336 from sqlalchemy.orm.properties import \
337 ColumnProperty,\
338 CompositeProperty,\
339 RelationshipProperty
341 class MyColumnComparator(ColumnProperty.Comparator):
342 def __eq__(self, other):
343 return self.__clause_element__() == other
345 class MyRelationshipComparator(RelationshipProperty.Comparator):
346 def any(self, expression):
347 "define the 'any' operation"
348 # ...
350 class MyCompositeComparator(CompositeProperty.Comparator):
351 def __gt__(self, other):
352 "redefine the 'greater than' operation"
354 return sql.and_(*[a>b for a, b in
355 zip(self.__clause_element__().clauses,
356 other.__composite_values__())])
359 # application of custom PropComparator subclasses
361 from sqlalchemy.orm import column_property, relationship, composite
362 from sqlalchemy import Column, String
364 class SomeMappedClass(Base):
365 some_column = column_property(Column("some_column", String),
366 comparator_factory=MyColumnComparator)
368 some_relationship = relationship(SomeOtherClass,
369 comparator_factory=MyRelationshipComparator)
371 some_composite = composite(
372 Column("a", String), Column("b", String),
373 comparator_factory=MyCompositeComparator
374 )
376 Note that for column-level operator redefinition, it's usually
377 simpler to define the operators at the Core level, using the
378 :attr:`.TypeEngine.comparator_factory` attribute. See
379 :ref:`types_operators` for more detail.
381 .. seealso::
383 :class:`.ColumnProperty.Comparator`
385 :class:`.RelationshipProperty.Comparator`
387 :class:`.CompositeProperty.Comparator`
389 :class:`.ColumnOperators`
391 :ref:`types_operators`
393 :attr:`.TypeEngine.comparator_factory`
395 """
397 __slots__ = "prop", "property", "_parententity", "_adapt_to_entity"
399 __visit_name__ = "orm_prop_comparator"
401 def __init__(
402 self,
403 prop,
404 parentmapper,
405 adapt_to_entity=None,
406 ):
407 self.prop = self.property = prop
408 self._parententity = adapt_to_entity or parentmapper
409 self._adapt_to_entity = adapt_to_entity
411 def __clause_element__(self):
412 raise NotImplementedError("%r" % self)
414 def _bulk_update_tuples(self, value):
415 """Receive a SQL expression that represents a value in the SET
416 clause of an UPDATE statement.
418 Return a tuple that can be passed to a :class:`_expression.Update`
419 construct.
421 """
423 return [(self.__clause_element__(), value)]
425 def adapt_to_entity(self, adapt_to_entity):
426 """Return a copy of this PropComparator which will use the given
427 :class:`.AliasedInsp` to produce corresponding expressions.
428 """
429 return self.__class__(self.prop, self._parententity, adapt_to_entity)
431 @property
432 def _parentmapper(self):
433 """legacy; this is renamed to _parententity to be
434 compatible with QueryableAttribute."""
435 return inspect(self._parententity).mapper
437 @property
438 def _propagate_attrs(self):
439 # this suits the case in coercions where we don't actually
440 # call ``__clause_element__()`` but still need to get
441 # resolved._propagate_attrs. See #6558.
442 return util.immutabledict(
443 {
444 "compile_state_plugin": "orm",
445 "plugin_subject": self._parentmapper,
446 }
447 )
449 @property
450 def adapter(self):
451 """Produce a callable that adapts column expressions
452 to suit an aliased version of this comparator.
454 """
455 if self._adapt_to_entity is None:
456 return None
457 else:
458 return self._adapt_to_entity._adapt_element
460 @property
461 def info(self):
462 return self.property.info
464 @staticmethod
465 def any_op(a, b, **kwargs):
466 return a.any(b, **kwargs)
468 @staticmethod
469 def has_op(a, b, **kwargs):
470 return a.has(b, **kwargs)
472 @staticmethod
473 def of_type_op(a, class_):
474 return a.of_type(class_)
476 def of_type(self, class_):
477 r"""Redefine this object in terms of a polymorphic subclass,
478 :func:`_orm.with_polymorphic` construct, or :func:`_orm.aliased`
479 construct.
481 Returns a new PropComparator from which further criterion can be
482 evaluated.
484 e.g.::
486 query.join(Company.employees.of_type(Engineer)).\
487 filter(Engineer.name=='foo')
489 :param \class_: a class or mapper indicating that criterion will be
490 against this specific subclass.
492 .. seealso::
494 :ref:`queryguide_join_onclause` - in the :ref:`queryguide_toplevel`
496 :ref:`inheritance_of_type`
498 """
500 return self.operate(PropComparator.of_type_op, class_)
502 def and_(self, *criteria):
503 """Add additional criteria to the ON clause that's represented by this
504 relationship attribute.
506 E.g.::
509 stmt = select(User).join(
510 User.addresses.and_(Address.email_address != 'foo')
511 )
513 stmt = select(User).options(
514 joinedload(User.addresses.and_(Address.email_address != 'foo'))
515 )
517 .. versionadded:: 1.4
519 .. seealso::
521 :ref:`orm_queryguide_join_on_augmented`
523 :ref:`loader_option_criteria`
525 :func:`.with_loader_criteria`
527 """
528 return self.operate(operators.and_, *criteria)
530 def any(self, criterion=None, **kwargs):
531 r"""Return true if this collection contains any member that meets the
532 given criterion.
534 The usual implementation of ``any()`` is
535 :meth:`.RelationshipProperty.Comparator.any`.
537 :param criterion: an optional ClauseElement formulated against the
538 member class' table or attributes.
540 :param \**kwargs: key/value pairs corresponding to member class
541 attribute names which will be compared via equality to the
542 corresponding values.
544 """
546 return self.operate(PropComparator.any_op, criterion, **kwargs)
548 def has(self, criterion=None, **kwargs):
549 r"""Return true if this element references a member which meets the
550 given criterion.
552 The usual implementation of ``has()`` is
553 :meth:`.RelationshipProperty.Comparator.has`.
555 :param criterion: an optional ClauseElement formulated against the
556 member class' table or attributes.
558 :param \**kwargs: key/value pairs corresponding to member class
559 attribute names which will be compared via equality to the
560 corresponding values.
562 """
564 return self.operate(PropComparator.has_op, criterion, **kwargs)
567class StrategizedProperty(MapperProperty):
568 """A MapperProperty which uses selectable strategies to affect
569 loading behavior.
571 There is a single strategy selected by default. Alternate
572 strategies can be selected at Query time through the usage of
573 ``StrategizedOption`` objects via the Query.options() method.
575 The mechanics of StrategizedProperty are used for every Query
576 invocation for every mapped attribute participating in that Query,
577 to determine first how the attribute will be rendered in SQL
578 and secondly how the attribute will retrieve a value from a result
579 row and apply it to a mapped object. The routines here are very
580 performance-critical.
582 """
584 __slots__ = (
585 "_strategies",
586 "strategy",
587 "_wildcard_token",
588 "_default_path_loader_key",
589 )
590 inherit_cache = True
591 strategy_wildcard_key = None
593 def _memoized_attr__wildcard_token(self):
594 return (
595 "%s:%s"
596 % (self.strategy_wildcard_key, path_registry._WILDCARD_TOKEN),
597 )
599 def _memoized_attr__default_path_loader_key(self):
600 return (
601 "loader",
602 (
603 "%s:%s"
604 % (self.strategy_wildcard_key, path_registry._DEFAULT_TOKEN),
605 ),
606 )
608 def _get_context_loader(self, context, path):
609 load = None
611 search_path = path[self]
613 # search among: exact match, "attr.*", "default" strategy
614 # if any.
615 for path_key in (
616 search_path._loader_key,
617 search_path._wildcard_path_loader_key,
618 search_path._default_path_loader_key,
619 ):
620 if path_key in context.attributes:
621 load = context.attributes[path_key]
622 break
624 return load
626 def _get_strategy(self, key):
627 try:
628 return self._strategies[key]
629 except KeyError:
630 pass
632 # run outside to prevent transfer of exception context
633 cls = self._strategy_lookup(self, *key)
634 # this previously was setting self._strategies[cls], that's
635 # a bad idea; should use strategy key at all times because every
636 # strategy has multiple keys at this point
637 self._strategies[key] = strategy = cls(self, key)
638 return strategy
640 def setup(self, context, query_entity, path, adapter, **kwargs):
641 loader = self._get_context_loader(context, path)
642 if loader and loader.strategy:
643 strat = self._get_strategy(loader.strategy)
644 else:
645 strat = self.strategy
646 strat.setup_query(
647 context, query_entity, path, loader, adapter, **kwargs
648 )
650 def create_row_processor(
651 self, context, query_entity, path, mapper, result, adapter, populators
652 ):
653 loader = self._get_context_loader(context, path)
654 if loader and loader.strategy:
655 strat = self._get_strategy(loader.strategy)
656 else:
657 strat = self.strategy
658 strat.create_row_processor(
659 context,
660 query_entity,
661 path,
662 loader,
663 mapper,
664 result,
665 adapter,
666 populators,
667 )
669 def do_init(self):
670 self._strategies = {}
671 self.strategy = self._get_strategy(self.strategy_key)
673 def post_instrument_class(self, mapper):
674 if (
675 not self.parent.non_primary
676 and not mapper.class_manager._attr_has_impl(self.key)
677 ):
678 self.strategy.init_class_attribute(mapper)
680 _all_strategies = collections.defaultdict(dict)
682 @classmethod
683 def strategy_for(cls, **kw):
684 def decorate(dec_cls):
685 # ensure each subclass of the strategy has its
686 # own _strategy_keys collection
687 if "_strategy_keys" not in dec_cls.__dict__:
688 dec_cls._strategy_keys = []
689 key = tuple(sorted(kw.items()))
690 cls._all_strategies[cls][key] = dec_cls
691 dec_cls._strategy_keys.append(key)
692 return dec_cls
694 return decorate
696 @classmethod
697 def _strategy_lookup(cls, requesting_property, *key):
698 requesting_property.parent._with_polymorphic_mappers
700 for prop_cls in cls.__mro__:
701 if prop_cls in cls._all_strategies:
702 strategies = cls._all_strategies[prop_cls]
703 try:
704 return strategies[key]
705 except KeyError:
706 pass
708 for property_type, strats in cls._all_strategies.items():
709 if key in strats:
710 intended_property_type = property_type
711 actual_strategy = strats[key]
712 break
713 else:
714 intended_property_type = None
715 actual_strategy = None
717 raise orm_exc.LoaderStrategyException(
718 cls,
719 requesting_property,
720 intended_property_type,
721 actual_strategy,
722 key,
723 )
726class ORMOption(ExecutableOption):
727 """Base class for option objects that are passed to ORM queries.
729 These options may be consumed by :meth:`.Query.options`,
730 :meth:`.Select.options`, or in a more general sense by any
731 :meth:`.Executable.options` method. They are interpreted at
732 statement compile time or execution time in modern use. The
733 deprecated :class:`.MapperOption` is consumed at ORM query construction
734 time.
736 .. versionadded:: 1.4
738 """
740 __slots__ = ()
742 _is_legacy_option = False
744 propagate_to_loaders = False
745 """if True, indicate this option should be carried along
746 to "secondary" SELECT statements that occur for relationship
747 lazy loaders as well as attribute load / refresh operations.
749 """
751 _is_compile_state = False
753 _is_criteria_option = False
755 _is_strategy_option = False
757 def _adapt_cached_option_to_uncached_option(self, context, uncached_opt):
758 """given "self" which is an option from a cached query, as well as the
759 corresponding option from the uncached version of the same query,
760 return the option we should use in a new query, in the context of a
761 loader strategy being asked to load related rows on behalf of that
762 cached query, which is assumed to be building a new query based on
763 entities passed to us from the cached query.
765 Currently this routine chooses between "self" and "uncached" without
766 manufacturing anything new. If the option is itself a loader strategy
767 option which has a path, that path needs to match to the entities being
768 passed to us by the cached query, so the :class:`_orm.Load` subclass
769 overrides this to return "self". For all other options, we return the
770 uncached form which may have changing state, such as a
771 with_loader_criteria() option which will very often have new state.
773 This routine could in the future involve
774 generating a new option based on both inputs if use cases arise,
775 such as if with_loader_criteria() needed to match up to
776 ``AliasedClass`` instances given in the parent query.
778 However, longer term it might be better to restructure things such that
779 ``AliasedClass`` entities are always matched up on their cache key,
780 instead of identity, in things like paths and such, so that this whole
781 issue of "the uncached option does not match the entities" goes away.
782 However this would make ``PathRegistry`` more complicated and difficult
783 to debug as well as potentially less performant in that it would be
784 hashing enormous cache keys rather than a simple AliasedInsp. UNLESS,
785 we could get cache keys overall to be reliably hashed into something
786 like an md5 key.
788 .. versionadded:: 1.4.41
791 """
792 if uncached_opt is not None:
793 return uncached_opt
794 else:
795 return self
798class CompileStateOption(HasCacheKey, ORMOption):
799 """base for :class:`.ORMOption` classes that affect the compilation of
800 a SQL query and therefore need to be part of the cache key.
802 .. note:: :class:`.CompileStateOption` is generally non-public and
803 should not be used as a base class for user-defined options; instead,
804 use :class:`.UserDefinedOption`, which is easier to use as it does not
805 interact with ORM compilation internals or caching.
807 :class:`.CompileStateOption` defines an internal attribute
808 ``_is_compile_state=True`` which has the effect of the ORM compilation
809 routines for SELECT and other statements will call upon these options when
810 a SQL string is being compiled. As such, these classes implement
811 :class:`.HasCacheKey` and need to provide robust ``_cache_key_traversal``
812 structures.
814 The :class:`.CompileStateOption` class is used to implement the ORM
815 :class:`.LoaderOption` and :class:`.CriteriaOption` classes.
817 .. versionadded:: 1.4.28
820 """
822 _is_compile_state = True
824 def process_compile_state(self, compile_state):
825 """Apply a modification to a given :class:`.CompileState`."""
827 def process_compile_state_replaced_entities(
828 self, compile_state, mapper_entities
829 ):
830 """Apply a modification to a given :class:`.CompileState`,
831 given entities that were replaced by with_only_columns() or
832 with_entities().
834 .. versionadded:: 1.4.19
836 """
839class LoaderOption(CompileStateOption):
840 """Describe a loader modification to an ORM statement at compilation time.
842 .. versionadded:: 1.4
844 """
846 def process_compile_state_replaced_entities(
847 self, compile_state, mapper_entities
848 ):
849 """Apply a modification to a given :class:`.CompileState`,
850 given entities that were replaced by with_only_columns() or
851 with_entities().
853 .. versionadded:: 1.4.19
855 """
856 self.process_compile_state(compile_state)
858 def process_compile_state(self, compile_state):
859 """Apply a modification to a given :class:`.CompileState`."""
862class CriteriaOption(CompileStateOption):
863 """Describe a WHERE criteria modification to an ORM statement at
864 compilation time.
866 .. versionadded:: 1.4
868 """
870 _is_criteria_option = True
872 def process_compile_state(self, compile_state):
873 """Apply a modification to a given :class:`.CompileState`."""
875 def get_global_criteria(self, attributes):
876 """update additional entity criteria options in the given
877 attributes dictionary.
879 """
882class UserDefinedOption(ORMOption):
883 """Base class for a user-defined option that can be consumed from the
884 :meth:`.SessionEvents.do_orm_execute` event hook.
886 """
888 _is_legacy_option = False
890 propagate_to_loaders = False
891 """if True, indicate this option should be carried along
892 to "secondary" Query objects produced during lazy loads
893 or refresh operations.
895 """
897 def __init__(self, payload=None):
898 self.payload = payload
901@util.deprecated_cls(
902 "1.4",
903 "The :class:`.MapperOption class is deprecated and will be removed "
904 "in a future release. For "
905 "modifications to queries on a per-execution basis, use the "
906 ":class:`.UserDefinedOption` class to establish state within a "
907 ":class:`.Query` or other Core statement, then use the "
908 ":meth:`.SessionEvents.before_orm_execute` hook to consume them.",
909 constructor=None,
910)
911class MapperOption(ORMOption):
912 """Describe a modification to a Query"""
914 _is_legacy_option = True
916 propagate_to_loaders = False
917 """if True, indicate this option should be carried along
918 to "secondary" Query objects produced during lazy loads
919 or refresh operations.
921 """
923 def process_query(self, query):
924 """Apply a modification to the given :class:`_query.Query`."""
926 def process_query_conditionally(self, query):
927 """same as process_query(), except that this option may not
928 apply to the given query.
930 This is typically applied during a lazy load or scalar refresh
931 operation to propagate options stated in the original Query to the
932 new Query being used for the load. It occurs for those options that
933 specify propagate_to_loaders=True.
935 """
937 self.process_query(query)
940class LoaderStrategy(object):
941 """Describe the loading behavior of a StrategizedProperty object.
943 The ``LoaderStrategy`` interacts with the querying process in three
944 ways:
946 * it controls the configuration of the ``InstrumentedAttribute``
947 placed on a class to handle the behavior of the attribute. this
948 may involve setting up class-level callable functions to fire
949 off a select operation when the attribute is first accessed
950 (i.e. a lazy load)
952 * it processes the ``QueryContext`` at statement construction time,
953 where it can modify the SQL statement that is being produced.
954 For example, simple column attributes will add their represented
955 column to the list of selected columns, a joined eager loader
956 may establish join clauses to add to the statement.
958 * It produces "row processor" functions at result fetching time.
959 These "row processor" functions populate a particular attribute
960 on a particular mapped instance.
962 """
964 __slots__ = (
965 "parent_property",
966 "is_class_level",
967 "parent",
968 "key",
969 "strategy_key",
970 "strategy_opts",
971 )
973 def __init__(self, parent, strategy_key):
974 self.parent_property = parent
975 self.is_class_level = False
976 self.parent = self.parent_property.parent
977 self.key = self.parent_property.key
978 self.strategy_key = strategy_key
979 self.strategy_opts = dict(strategy_key)
981 def init_class_attribute(self, mapper):
982 pass
984 def setup_query(
985 self, compile_state, query_entity, path, loadopt, adapter, **kwargs
986 ):
987 """Establish column and other state for a given QueryContext.
989 This method fulfills the contract specified by MapperProperty.setup().
991 StrategizedProperty delegates its setup() method
992 directly to this method.
994 """
996 def create_row_processor(
997 self,
998 context,
999 query_entity,
1000 path,
1001 loadopt,
1002 mapper,
1003 result,
1004 adapter,
1005 populators,
1006 ):
1007 """Establish row processing functions for a given QueryContext.
1009 This method fulfills the contract specified by
1010 MapperProperty.create_row_processor().
1012 StrategizedProperty delegates its create_row_processor() method
1013 directly to this method.
1015 """
1017 def __str__(self):
1018 return str(self.parent_property)