Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
relationships.py3509 linesDownload Raw Back to orm
1# orm/relationships.py
2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8"""Heuristics related to join conditions as used in
9:func:`_orm.relationship`.
10
11Provides the :class:`.JoinCondition` object, which encapsulates
12SQL annotation and aliasing behavior focused on the `primaryjoin`
13and `secondaryjoin` aspects of :func:`_orm.relationship`.
14
15"""
16from __future__ import annotations
17
18import collections
19from collections import abc
20import dataclasses
21import inspect as _py_inspect
22import itertools
23import re
24import typing
25from typing import Any
26from typing import Callable
27from typing import cast
28from typing import Collection
29from typing import Dict
30from typing import FrozenSet
31from typing import Generic
32from typing import Iterable
33from typing import Iterator
34from typing import List
35from typing import NamedTuple
36from typing import NoReturn
37from typing import Optional
38from typing import Sequence
39from typing import Set
40from typing import Tuple
41from typing import Type
42from typing import TypeVar
43from typing import Union
44import weakref
45
46from . import attributes
47from . import strategy_options
48from ._typing import insp_is_aliased_class
49from ._typing import is_has_collection_adapter
50from .base import _DeclarativeMapped
51from .base import _is_mapped_class
52from .base import class_mapper
53from .base import DynamicMapped
54from .base import LoaderCallableStatus
55from .base import PassiveFlag
56from .base import state_str
57from .base import WriteOnlyMapped
58from .interfaces import _AttributeOptions
59from .interfaces import _IntrospectsAnnotations
60from .interfaces import MANYTOMANY
61from .interfaces import MANYTOONE
62from .interfaces import ONETOMANY
63from .interfaces import PropComparator
64from .interfaces import RelationshipDirection
65from .interfaces import StrategizedProperty
66from .util import _orm_annotate
67from .util import _orm_deannotate
68from .util import CascadeOptions
69from .. import exc as sa_exc
70from .. import Exists
71from .. import log
72from .. import schema
73from .. import sql
74from .. import util
75from ..inspection import inspect
76from ..sql import coercions
77from ..sql import expression
78from ..sql import operators
79from ..sql import roles
80from ..sql import visitors
81from ..sql._typing import _ColumnExpressionArgument
82from ..sql._typing import _HasClauseElement
83from ..sql.annotation import _safe_annotate
84from ..sql.elements import ColumnClause
85from ..sql.elements import ColumnElement
86from ..sql.util import _deep_annotate
87from ..sql.util import _deep_deannotate
88from ..sql.util import _shallow_annotate
89from ..sql.util import adapt_criterion_to_null
90from ..sql.util import ClauseAdapter
91from ..sql.util import join_condition
92from ..sql.util import selectables_overlap
93from ..sql.util import visit_binary_product
94from ..util.typing import de_optionalize_union_types
95from ..util.typing import Literal
96from ..util.typing import resolve_name_to_real_class_name
97
98if typing.TYPE_CHECKING:
99    from ._typing import _EntityType
100    from ._typing import _ExternalEntityType
101    from ._typing import _IdentityKeyType
102    from ._typing import _InstanceDict
103    from ._typing import _InternalEntityType
104    from ._typing import _O
105    from ._typing import _RegistryType
106    from .base import Mapped
107    from .clsregistry import _class_resolver
108    from .clsregistry import _ModNS
109    from .decl_base import _ClassScanMapperConfig
110    from .dependency import DependencyProcessor
111    from .mapper import Mapper
112    from .query import Query
113    from .session import Session
114    from .state import InstanceState
115    from .strategies import LazyLoader
116    from .util import AliasedClass
117    from .util import AliasedInsp
118    from ..sql._typing import _CoreAdapterProto
119    from ..sql._typing import _EquivalentColumnMap
120    from ..sql._typing import _InfoType
121    from ..sql.annotation import _AnnotationDict
122    from ..sql.annotation import SupportsAnnotations
123    from ..sql.elements import BinaryExpression
124    from ..sql.elements import BindParameter
125    from ..sql.elements import ClauseElement
126    from ..sql.schema import Table
127    from ..sql.selectable import FromClause
128    from ..util.typing import _AnnotationScanType
129    from ..util.typing import RODescriptorReference
130
131_T = TypeVar("_T", bound=Any)
132_T1 = TypeVar("_T1", bound=Any)
133_T2 = TypeVar("_T2", bound=Any)
134
135_PT = TypeVar("_PT", bound=Any)
136
137_PT2 = TypeVar("_PT2", bound=Any)
138
139
140_RelationshipArgumentType = Union[
141    str,
142    Type[_T],
143    Callable[[], Type[_T]],
144    "Mapper[_T]",
145    "AliasedClass[_T]",
146    Callable[[], "Mapper[_T]"],
147    Callable[[], "AliasedClass[_T]"],
148]
149
150_LazyLoadArgumentType = Literal[
151    "select",
152    "joined",
153    "selectin",
154    "subquery",
155    "raise",
156    "raise_on_sql",
157    "noload",
158    "immediate",
159    "write_only",
160    "dynamic",
161    True,
162    False,
163    None,
164]
165
166
167_RelationshipJoinConditionArgument = Union[
168    str, _ColumnExpressionArgument[bool]
169]
170_RelationshipSecondaryArgument = Union[
171    "FromClause", str, Callable[[], "FromClause"]
172]
173_ORMOrderByArgument = Union[
174    Literal[False],
175    str,
176    _ColumnExpressionArgument[Any],
177    Callable[[], _ColumnExpressionArgument[Any]],
178    Callable[[], Iterable[_ColumnExpressionArgument[Any]]],
179    Iterable[Union[str, _ColumnExpressionArgument[Any]]],
180]
181ORMBackrefArgument = Union[str, Tuple[str, Dict[str, Any]]]
182
183_ORMColCollectionElement = Union[
184    ColumnClause[Any],
185    _HasClauseElement[Any],
186    roles.DMLColumnRole,
187    "Mapped[Any]",
188]
189_ORMColCollectionArgument = Union[
190    str,
191    Sequence[_ORMColCollectionElement],
192    Callable[[], Sequence[_ORMColCollectionElement]],
193    Callable[[], _ORMColCollectionElement],
194    _ORMColCollectionElement,
195]
196
197
198_CEA = TypeVar("_CEA", bound=_ColumnExpressionArgument[Any])
199
200_CE = TypeVar("_CE", bound="ColumnElement[Any]")
201
202
203_ColumnPairIterable = Iterable[Tuple[ColumnElement[Any], ColumnElement[Any]]]
204
205_ColumnPairs = Sequence[Tuple[ColumnElement[Any], ColumnElement[Any]]]
206
207_MutableColumnPairs = List[Tuple[ColumnElement[Any], ColumnElement[Any]]]
208
209
210def remote(expr: _CEA) -> _CEA:
211    """Annotate a portion of a primaryjoin expression
212    with a 'remote' annotation.
213
214    See the section :ref:`relationship_custom_foreign` for a
215    description of use.
216
217    .. seealso::
218
219        :ref:`relationship_custom_foreign`
220
221        :func:`.foreign`
222
223    """
224    return _annotate_columns(  # type: ignore
225        coercions.expect(roles.ColumnArgumentRole, expr), {"remote": True}
226    )
227
228
229def foreign(expr: _CEA) -> _CEA:
230    """Annotate a portion of a primaryjoin expression
231    with a 'foreign' annotation.
232
233    See the section :ref:`relationship_custom_foreign` for a
234    description of use.
235
236    .. seealso::
237
238        :ref:`relationship_custom_foreign`
239
240        :func:`.remote`
241
242    """
243
244    return _annotate_columns(  # type: ignore
245        coercions.expect(roles.ColumnArgumentRole, expr), {"foreign": True}
246    )
247
248
249@dataclasses.dataclass
250class _RelationshipArg(Generic[_T1, _T2]):
251    """stores a user-defined parameter value that must be resolved and
252    parsed later at mapper configuration time.
253
254    """
255
256    __slots__ = "name", "argument", "resolved"
257    name: str
258    argument: _T1
259    resolved: Optional[_T2]
260
261    def _is_populated(self) -> bool:
262        return self.argument is not None
263
264    def _resolve_against_registry(
265        self, clsregistry_resolver: Callable[[str, bool], _class_resolver]
266    ) -> None:
267        attr_value = self.argument
268
269        if isinstance(attr_value, str):
270            self.resolved = clsregistry_resolver(
271                attr_value, self.name == "secondary"
272            )()
273        elif callable(attr_value) and not _is_mapped_class(attr_value):
274            self.resolved = attr_value()
275        else:
276            self.resolved = attr_value
277
278
279_RelationshipOrderByArg = Union[Literal[False], Tuple[ColumnElement[Any], ...]]
280
281
282class _RelationshipArgs(NamedTuple):
283    """stores user-passed parameters that are resolved at mapper configuration
284    time.
285
286    """
287
288    secondary: _RelationshipArg[
289        Optional[_RelationshipSecondaryArgument],
290        Optional[FromClause],
291    ]
292    primaryjoin: _RelationshipArg[
293        Optional[_RelationshipJoinConditionArgument],
294        Optional[ColumnElement[Any]],
295    ]
296    secondaryjoin: _RelationshipArg[
297        Optional[_RelationshipJoinConditionArgument],
298        Optional[ColumnElement[Any]],
299    ]
300    order_by: _RelationshipArg[_ORMOrderByArgument, _RelationshipOrderByArg]
301    foreign_keys: _RelationshipArg[
302        Optional[_ORMColCollectionArgument], Set[ColumnElement[Any]]
303    ]
304    remote_side: _RelationshipArg[
305        Optional[_ORMColCollectionArgument], Set[ColumnElement[Any]]
306    ]
307
308
309@log.class_logger
310class RelationshipProperty(
311    _IntrospectsAnnotations, StrategizedProperty[_T], log.Identified
312):
313    """Describes an object property that holds a single item or list
314    of items that correspond to a related database table.
315
316    Public constructor is the :func:`_orm.relationship` function.
317
318    .. seealso::
319
320        :ref:`relationship_config_toplevel`
321
322    """
323
324    strategy_wildcard_key = strategy_options._RELATIONSHIP_TOKEN
325    inherit_cache = True
326    """:meta private:"""
327
328    _links_to_entity = True
329    _is_relationship = True
330
331    _overlaps: Sequence[str]
332
333    _lazy_strategy: LazyLoader
334
335    _persistence_only = dict(
336        passive_deletes=False,
337        passive_updates=True,
338        enable_typechecks=True,
339        active_history=False,
340        cascade_backrefs=False,
341    )
342
343    _dependency_processor: Optional[DependencyProcessor] = None
344
345    primaryjoin: ColumnElement[bool]
346    secondaryjoin: Optional[ColumnElement[bool]]
347    secondary: Optional[FromClause]
348    _join_condition: JoinCondition
349    order_by: _RelationshipOrderByArg
350
351    _user_defined_foreign_keys: Set[ColumnElement[Any]]
352    _calculated_foreign_keys: Set[ColumnElement[Any]]
353
354    remote_side: Set[ColumnElement[Any]]
355    local_columns: Set[ColumnElement[Any]]
356
357    synchronize_pairs: _ColumnPairs
358    secondary_synchronize_pairs: Optional[_ColumnPairs]
359
360    local_remote_pairs: Optional[_ColumnPairs]
361
362    direction: RelationshipDirection
363
364    _init_args: _RelationshipArgs
365
366    def __init__(
367        self,
368        argument: Optional[_RelationshipArgumentType[_T]] = None,
369        secondary: Optional[_RelationshipSecondaryArgument] = None,
370        *,
371        uselist: Optional[bool] = None,
372        collection_class: Optional[
373            Union[Type[Collection[Any]], Callable[[], Collection[Any]]]
374        ] = None,
375        primaryjoin: Optional[_RelationshipJoinConditionArgument] = None,
376        secondaryjoin: Optional[_RelationshipJoinConditionArgument] = None,
377        back_populates: Optional[str] = None,
378        order_by: _ORMOrderByArgument = False,
379        backref: Optional[ORMBackrefArgument] = None,
380        overlaps: Optional[str] = None,
381        post_update: bool = False,
382        cascade: str = "save-update, merge",
383        viewonly: bool = False,
384        attribute_options: Optional[_AttributeOptions] = None,
385        lazy: _LazyLoadArgumentType = "select",
386        passive_deletes: Union[Literal["all"], bool] = False,
387        passive_updates: bool = True,
388        active_history: bool = False,
389        enable_typechecks: bool = True,
390        foreign_keys: Optional[_ORMColCollectionArgument] = None,
391        remote_side: Optional[_ORMColCollectionArgument] = None,
392        join_depth: Optional[int] = None,
393        comparator_factory: Optional[
394            Type[RelationshipProperty.Comparator[Any]]
395        ] = None,
396        single_parent: bool = False,
397        innerjoin: bool = False,
398        distinct_target_key: Optional[bool] = None,
399        load_on_pending: bool = False,
400        query_class: Optional[Type[Query[Any]]] = None,
401        info: Optional[_InfoType] = None,
402        omit_join: Literal[None, False] = None,
403        sync_backref: Optional[bool] = None,
404        doc: Optional[str] = None,
405        bake_queries: Literal[True] = True,
406        cascade_backrefs: Literal[False] = False,
407        _local_remote_pairs: Optional[_ColumnPairs] = None,
408        _legacy_inactive_history_style: bool = False,
409    ):
410        super().__init__(attribute_options=attribute_options)
411
412        self.uselist = uselist
413        self.argument = argument
414
415        self._init_args = _RelationshipArgs(
416            _RelationshipArg("secondary", secondary, None),
417            _RelationshipArg("primaryjoin", primaryjoin, None),
418            _RelationshipArg("secondaryjoin", secondaryjoin, None),
419            _RelationshipArg("order_by", order_by, None),
420            _RelationshipArg("foreign_keys", foreign_keys, None),
421            _RelationshipArg("remote_side", remote_side, None),
422        )
423
424        self.post_update = post_update
425        self.viewonly = viewonly
426        if viewonly:
427            self._warn_for_persistence_only_flags(
428                passive_deletes=passive_deletes,
429                passive_updates=passive_updates,
430                enable_typechecks=enable_typechecks,
431                active_history=active_history,
432                cascade_backrefs=cascade_backrefs,
433            )
434        if viewonly and sync_backref:
435            raise sa_exc.ArgumentError(
436                "sync_backref and viewonly cannot both be True"
437            )
438        self.sync_backref = sync_backref
439        self.lazy = lazy
440        self.single_parent = single_parent
441        self.collection_class = collection_class
442        self.passive_deletes = passive_deletes
443
444        if cascade_backrefs:
445            raise sa_exc.ArgumentError(
446                "The 'cascade_backrefs' parameter passed to "
447                "relationship() may only be set to False."
448            )
449
450        self.passive_updates = passive_updates
451        self.enable_typechecks = enable_typechecks
452        self.query_class = query_class
453        self.innerjoin = innerjoin
454        self.distinct_target_key = distinct_target_key
455        self.doc = doc
456        self.active_history = active_history
457        self._legacy_inactive_history_style = _legacy_inactive_history_style
458
459        self.join_depth = join_depth
460        if omit_join:
461            util.warn(
462                "setting omit_join to True is not supported; selectin "
463                "loading of this relationship may not work correctly if this "
464                "flag is set explicitly.  omit_join optimization is "
465                "automatically detected for conditions under which it is "
466                "supported."
467            )
468
469        self.omit_join = omit_join
470        self.local_remote_pairs = _local_remote_pairs
471        self.load_on_pending = load_on_pending
472        self.comparator_factory = (
473            comparator_factory or RelationshipProperty.Comparator
474        )
475        util.set_creation_order(self)
476
477        if info is not None:
478            self.info.update(info)
479
480        self.strategy_key = (("lazy", self.lazy),)
481
482        self._reverse_property: Set[RelationshipProperty[Any]] = set()
483
484        if overlaps:
485            self._overlaps = set(re.split(r"\s*,\s*", overlaps))  # type: ignore  # noqa: E501
486        else:
487            self._overlaps = ()
488
489        self.cascade = cascade
490
491        self.back_populates = back_populates
492
493        if self.back_populates:
494            if backref:
495                raise sa_exc.ArgumentError(
496                    "backref and back_populates keyword arguments "
497                    "are mutually exclusive"
498                )
499            self.backref = None
500        else:
501            self.backref = backref
502
503    def _warn_for_persistence_only_flags(self, **kw: Any) -> None:
504        for k, v in kw.items():
505            if v != self._persistence_only[k]:
506                # we are warning here rather than warn deprecated as this is a
507                # configuration mistake, and Python shows regular warnings more
508                # aggressively than deprecation warnings by default. Unlike the
509                # case of setting viewonly with cascade, the settings being
510                # warned about here are not actively doing the wrong thing
511                # against viewonly=True, so it is not as urgent to have these
512                # raise an error.
513                util.warn(
514                    "Setting %s on relationship() while also "
515                    "setting viewonly=True does not make sense, as a "
516                    "viewonly=True relationship does not perform persistence "
517                    "operations. This configuration may raise an error "
518                    "in a future release." % (k,)
519                )
520
521    def instrument_class(self, mapper: Mapper[Any]) -> None:
522        attributes.register_descriptor(
523            mapper.class_,
524            self.key,
525            comparator=self.comparator_factory(self, mapper),
526            parententity=mapper,
527            doc=self.doc,
528        )
529
530    class Comparator(util.MemoizedSlots, PropComparator[_PT]):
531        """Produce boolean, comparison, and other operators for
532        :class:`.RelationshipProperty` attributes.
533
534        See the documentation for :class:`.PropComparator` for a brief
535        overview of ORM level operator definition.
536
537        .. seealso::
538
539            :class:`.PropComparator`
540
541            :class:`.ColumnProperty.Comparator`
542
543            :class:`.ColumnOperators`
544
545            :ref:`types_operators`
546
547            :attr:`.TypeEngine.comparator_factory`
548
549        """
550
551        __slots__ = (
552            "entity",
553            "mapper",
554            "property",
555            "_of_type",
556            "_extra_criteria",
557        )
558
559        prop: RODescriptorReference[RelationshipProperty[_PT]]
560        _of_type: Optional[_EntityType[_PT]]
561
562        def __init__(
563            self,
564            prop: RelationshipProperty[_PT],
565            parentmapper: _InternalEntityType[Any],
566            adapt_to_entity: Optional[AliasedInsp[Any]] = None,
567            of_type: Optional[_EntityType[_PT]] = None,
568            extra_criteria: Tuple[ColumnElement[bool], ...] = (),
569        ):
570            """Construction of :class:`.RelationshipProperty.Comparator`
571            is internal to the ORM's attribute mechanics.
572
573            """
574            self.prop = prop
575            self._parententity = parentmapper
576            self._adapt_to_entity = adapt_to_entity
577            if of_type:
578                self._of_type = of_type
579            else:
580                self._of_type = None
581            self._extra_criteria = extra_criteria
582
583        def adapt_to_entity(
584            self, adapt_to_entity: AliasedInsp[Any]
585        ) -> RelationshipProperty.Comparator[Any]:
586            return self.__class__(
587                self.prop,
588                self._parententity,
589                adapt_to_entity=adapt_to_entity,
590                of_type=self._of_type,
591            )
592
593        entity: _InternalEntityType[_PT]
594        """The target entity referred to by this
595        :class:`.RelationshipProperty.Comparator`.
596
597        This is either a :class:`_orm.Mapper` or :class:`.AliasedInsp`
598        object.
599
600        This is the "target" or "remote" side of the
601        :func:`_orm.relationship`.
602
603        """
604
605        mapper: Mapper[_PT]
606        """The target :class:`_orm.Mapper` referred to by this
607        :class:`.RelationshipProperty.Comparator`.
608
609        This is the "target" or "remote" side of the
610        :func:`_orm.relationship`.
611
612        """
613
614        def _memoized_attr_entity(self) -> _InternalEntityType[_PT]:
615            if self._of_type:
616                return inspect(self._of_type)  # type: ignore
617            else:
618                return self.prop.entity
619
620        def _memoized_attr_mapper(self) -> Mapper[_PT]:
621            return self.entity.mapper
622
623        def _source_selectable(self) -> FromClause:
624            if self._adapt_to_entity:
625                return self._adapt_to_entity.selectable
626            else:
627                return self.property.parent._with_polymorphic_selectable
628
629        def __clause_element__(self) -> ColumnElement[bool]:
630            adapt_from = self._source_selectable()
631            if self._of_type:
632                of_type_entity = inspect(self._of_type)
633            else:
634                of_type_entity = None
635
636            (
637                pj,
638                sj,
639                source,
640                dest,
641                secondary,
642                target_adapter,
643            ) = self.prop._create_joins(
644                source_selectable=adapt_from,
645                source_polymorphic=True,
646                of_type_entity=of_type_entity,
647                alias_secondary=True,
648                extra_criteria=self._extra_criteria,
649            )
650            if sj is not None:
651                return pj & sj
652            else:
653                return pj
654
655        def of_type(self, class_: _EntityType[Any]) -> PropComparator[_PT]:
656            r"""Redefine this object in terms of a polymorphic subclass.
657
658            See :meth:`.PropComparator.of_type` for an example.
659
660
661            """
662            return RelationshipProperty.Comparator(
663                self.prop,
664                self._parententity,
665                adapt_to_entity=self._adapt_to_entity,
666                of_type=class_,
667                extra_criteria=self._extra_criteria,
668            )
669
670        def and_(
671            self, *criteria: _ColumnExpressionArgument[bool]
672        ) -> PropComparator[Any]:
673            """Add AND criteria.
674
675            See :meth:`.PropComparator.and_` for an example.
676
677            .. versionadded:: 1.4
678
679            """
680            exprs = tuple(
681                coercions.expect(roles.WhereHavingRole, clause)
682                for clause in util.coerce_generator_arg(criteria)
683            )
684
685            return RelationshipProperty.Comparator(
686                self.prop,
687                self._parententity,
688                adapt_to_entity=self._adapt_to_entity,
689                of_type=self._of_type,
690                extra_criteria=self._extra_criteria + exprs,
691            )
692
693        def in_(self, other: Any) -> NoReturn:
694            """Produce an IN clause - this is not implemented
695            for :func:`_orm.relationship`-based attributes at this time.
696
697            """
698            raise NotImplementedError(
699                "in_() not yet supported for "
700                "relationships.  For a simple "
701                "many-to-one, use in_() against "
702                "the set of foreign key values."
703            )
704
705        # https://github.com/python/mypy/issues/4266
706        __hash__ = None  # type: ignore
707
708        def __eq__(self, other: Any) -> ColumnElement[bool]:  # type: ignore[override]  # noqa: E501
709            """Implement the ``==`` operator.
710
711            In a many-to-one context, such as:
712
713            .. sourcecode:: text
714
715              MyClass.some_prop == <some object>
716
717            this will typically produce a
718            clause such as:
719
720            .. sourcecode:: text
721
722              mytable.related_id == <some id>
723
724            Where ``<some id>`` is the primary key of the given
725            object.
726
727            The ``==`` operator provides partial functionality for non-
728            many-to-one comparisons:
729
730            * Comparisons against collections are not supported.
731              Use :meth:`~.RelationshipProperty.Comparator.contains`.
732            * Compared to a scalar one-to-many, will produce a
733              clause that compares the target columns in the parent to
734              the given target.
735            * Compared to a scalar many-to-many, an alias
736              of the association table will be rendered as
737              well, forming a natural join that is part of the
738              main body of the query. This will not work for
739              queries that go beyond simple AND conjunctions of
740              comparisons, such as those which use OR. Use
741              explicit joins, outerjoins, or
742              :meth:`~.RelationshipProperty.Comparator.has` for
743              more comprehensive non-many-to-one scalar
744              membership tests.
745            * Comparisons against ``None`` given in a one-to-many
746              or many-to-many context produce a NOT EXISTS clause.
747
748            """
749            if other is None or isinstance(other, expression.Null):
750                if self.property.direction in [ONETOMANY, MANYTOMANY]:
751                    return ~self._criterion_exists()
752                else:
753                    return _orm_annotate(
754                        self.property._optimized_compare(
755                            None, adapt_source=self.adapter
756                        )
757                    )
758            elif self.property.uselist:
759                raise sa_exc.InvalidRequestError(
760                    "Can't compare a collection to an object or collection; "
761                    "use contains() to test for membership."
762                )
763            else:
764                return _orm_annotate(
765                    self.property._optimized_compare(
766                        other, adapt_source=self.adapter
767                    )
768                )
769
770        def _criterion_exists(
771            self,
772            criterion: Optional[_ColumnExpressionArgument[bool]] = None,
773            **kwargs: Any,
774        ) -> Exists:
775            where_criteria = (
776                coercions.expect(roles.WhereHavingRole, criterion)
777                if criterion is not None
778                else None
779            )
780
781            if getattr(self, "_of_type", None):
782                info: Optional[_InternalEntityType[Any]] = inspect(
783                    self._of_type
784                )
785                assert info is not None
786                target_mapper, to_selectable, is_aliased_class = (
787                    info.mapper,
788                    info.selectable,
789                    info.is_aliased_class,
790                )
791                if self.property._is_self_referential and not is_aliased_class:
792                    to_selectable = to_selectable._anonymous_fromclause()
793
794                single_crit = target_mapper._single_table_criterion
795                if single_crit is not None:
796                    if where_criteria is not None:
797                        where_criteria = single_crit & where_criteria
798                    else:
799                        where_criteria = single_crit
800            else:
801                is_aliased_class = False
802                to_selectable = None
803
804            if self.adapter:
805                source_selectable = self._source_selectable()
806            else:
807                source_selectable = None
808
809            (
810                pj,
811                sj,
812                source,
813                dest,
814                secondary,
815                target_adapter,
816            ) = self.property._create_joins(
817                dest_selectable=to_selectable,
818                source_selectable=source_selectable,
819            )
820
821            for k in kwargs:
822                crit = getattr(self.property.mapper.class_, k) == kwargs[k]
823                if where_criteria is None:
824                    where_criteria = crit
825                else:
826                    where_criteria = where_criteria & crit
827
828            # annotate the *local* side of the join condition, in the case
829            # of pj + sj this is the full primaryjoin, in the case of just
830            # pj its the local side of the primaryjoin.
831            if sj is not None:
832                j = _orm_annotate(pj) & sj
833            else:
834                j = _orm_annotate(pj, exclude=self.property.remote_side)
835
836            if (
837                where_criteria is not None
838                and target_adapter
839                and not is_aliased_class
840            ):
841                # limit this adapter to annotated only?
842                where_criteria = target_adapter.traverse(where_criteria)
843
844            # only have the "joined left side" of what we
845            # return be subject to Query adaption.  The right
846            # side of it is used for an exists() subquery and
847            # should not correlate or otherwise reach out
848            # to anything in the enclosing query.
849            if where_criteria is not None:
850                where_criteria = where_criteria._annotate(
851                    {"no_replacement_traverse": True}
852                )
853
854            crit = j & sql.True_._ifnone(where_criteria)
855
856            if secondary is not None:
857                ex = (
858                    sql.exists(1)
859                    .where(crit)
860                    .select_from(dest, secondary)
861                    .correlate_except(dest, secondary)
862                )
863            else:
864                ex = (
865                    sql.exists(1)
866                    .where(crit)
867                    .select_from(dest)
868                    .correlate_except(dest)
869                )
870            return ex
871
872        def any(
873            self,
874            criterion: Optional[_ColumnExpressionArgument[bool]] = None,
875            **kwargs: Any,
876        ) -> ColumnElement[bool]:
877            """Produce an expression that tests a collection against
878            particular criterion, using EXISTS.
879
880            An expression like::
881
882                session.query(MyClass).filter(
883                    MyClass.somereference.any(SomeRelated.x == 2)
884                )
885
886            Will produce a query like:
887
888            .. sourcecode:: sql
889
890                SELECT * FROM my_table WHERE
891                EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id
892                AND related.x=2)
893
894            Because :meth:`~.RelationshipProperty.Comparator.any` uses
895            a correlated subquery, its performance is not nearly as
896            good when compared against large target tables as that of
897            using a join.
898
899            :meth:`~.RelationshipProperty.Comparator.any` is particularly
900            useful for testing for empty collections::
901
902                session.query(MyClass).filter(~MyClass.somereference.any())
903
904            will produce:
905
906            .. sourcecode:: sql
907
908                SELECT * FROM my_table WHERE
909                NOT (EXISTS (SELECT 1 FROM related WHERE
910                related.my_id=my_table.id))
911
912            :meth:`~.RelationshipProperty.Comparator.any` is only
913            valid for collections, i.e. a :func:`_orm.relationship`
914            that has ``uselist=True``.  For scalar references,
915            use :meth:`~.RelationshipProperty.Comparator.has`.
916
917            """
918            if not self.property.uselist:
919                raise sa_exc.InvalidRequestError(
920                    "'any()' not implemented for scalar "
921                    "attributes. Use has()."
922                )
923
924            return self._criterion_exists(criterion, **kwargs)
925
926        def has(
927            self,
928            criterion: Optional[_ColumnExpressionArgument[bool]] = None,
929            **kwargs: Any,
930        ) -> ColumnElement[bool]:
931            """Produce an expression that tests a scalar reference against
932            particular criterion, using EXISTS.
933
934            An expression like::
935
936                session.query(MyClass).filter(
937                    MyClass.somereference.has(SomeRelated.x == 2)
938                )
939
940            Will produce a query like:
941
942            .. sourcecode:: sql
943
944                SELECT * FROM my_table WHERE
945                EXISTS (SELECT 1 FROM related WHERE
946                related.id==my_table.related_id AND related.x=2)
947
948            Because :meth:`~.RelationshipProperty.Comparator.has` uses
949            a correlated subquery, its performance is not nearly as
950            good when compared against large target tables as that of
951            using a join.
952
953            :meth:`~.RelationshipProperty.Comparator.has` is only
954            valid for scalar references, i.e. a :func:`_orm.relationship`
955            that has ``uselist=False``.  For collection references,
956            use :meth:`~.RelationshipProperty.Comparator.any`.
957
958            """
959            if self.property.uselist:
960                raise sa_exc.InvalidRequestError(
961                    "'has()' not implemented for collections. Use any()."
962                )
963            return self._criterion_exists(criterion, **kwargs)
964
965        def contains(
966            self, other: _ColumnExpressionArgument[Any], **kwargs: Any
967        ) -> ColumnElement[bool]:
968            """Return a simple expression that tests a collection for
969            containment of a particular item.
970
971            :meth:`~.RelationshipProperty.Comparator.contains` is
972            only valid for a collection, i.e. a
973            :func:`_orm.relationship` that implements
974            one-to-many or many-to-many with ``uselist=True``.
975
976            When used in a simple one-to-many context, an
977            expression like::
978
979                MyClass.contains(other)
980
981            Produces a clause like:
982
983            .. sourcecode:: sql
984
985                mytable.id == <some id>
986
987            Where ``<some id>`` is the value of the foreign key
988            attribute on ``other`` which refers to the primary
989            key of its parent object. From this it follows that
990            :meth:`~.RelationshipProperty.Comparator.contains` is
991            very useful when used with simple one-to-many
992            operations.
993
994            For many-to-many operations, the behavior of
995            :meth:`~.RelationshipProperty.Comparator.contains`
996            has more caveats. The association table will be
997            rendered in the statement, producing an "implicit"
998            join, that is, includes multiple tables in the FROM
999            clause which are equated in the WHERE clause::
1000
1001                query(MyClass).filter(MyClass.contains(other))
1002
1003            Produces a query like:
1004
1005            .. sourcecode:: sql
1006
1007                SELECT * FROM my_table, my_association_table AS
1008                my_association_table_1 WHERE
1009                my_table.id = my_association_table_1.parent_id
1010                AND my_association_table_1.child_id = <some id>
1011
1012            Where ``<some id>`` would be the primary key of
1013            ``other``. From the above, it is clear that
1014            :meth:`~.RelationshipProperty.Comparator.contains`
1015            will **not** work with many-to-many collections when
1016            used in queries that move beyond simple AND
1017            conjunctions, such as multiple
1018            :meth:`~.RelationshipProperty.Comparator.contains`
1019            expressions joined by OR. In such cases subqueries or
1020            explicit "outer joins" will need to be used instead.
1021            See :meth:`~.RelationshipProperty.Comparator.any` for
1022            a less-performant alternative using EXISTS, or refer
1023            to :meth:`_query.Query.outerjoin`
1024            as well as :ref:`orm_queryguide_joins`
1025            for more details on constructing outer joins.
1026
1027            kwargs may be ignored by this operator but are required for API
1028            conformance.
1029            """
1030            if not self.prop.uselist:
1031                raise sa_exc.InvalidRequestError(
1032                    "'contains' not implemented for scalar "
1033                    "attributes.  Use =="
1034                )
1035
1036            clause = self.prop._optimized_compare(
1037                other, adapt_source=self.adapter
1038            )
1039
1040            if self.prop.secondaryjoin is not None:
1041                clause.negation_clause = self.__negated_contains_or_equals(
1042                    other
1043                )
1044
1045            return clause
1046
1047        def __negated_contains_or_equals(
1048            self, other: Any
1049        ) -> ColumnElement[bool]:
1050            if self.prop.direction == MANYTOONE:
1051                state = attributes.instance_state(other)
1052
1053                def state_bindparam(
1054                    local_col: ColumnElement[Any],
1055                    state: InstanceState[Any],
1056                    remote_col: ColumnElement[Any],
1057                ) -> BindParameter[Any]:
1058                    dict_ = state.dict
1059                    return sql.bindparam(
1060                        local_col.key,
1061                        type_=local_col.type,
1062                        unique=True,
1063                        callable_=self.prop._get_attr_w_warn_on_none(
1064                            self.prop.mapper, state, dict_, remote_col
1065                        ),
1066                    )
1067
1068                def adapt(col: _CE) -> _CE:
1069                    if self.adapter:
1070                        return self.adapter(col)
1071                    else:
1072                        return col
1073
1074                if self.property._use_get:
1075                    return sql.and_(
1076                        *[
1077                            sql.or_(
1078                                adapt(x)
1079                                != state_bindparam(adapt(x), state, y),
1080                                adapt(x) == None,
1081                            )
1082                            for (x, y) in self.property.local_remote_pairs
1083                        ]
1084                    )
1085
1086            criterion = sql.and_(
1087                *[
1088                    x == y
1089                    for (x, y) in zip(
1090                        self.property.mapper.primary_key,
1091                        self.property.mapper.primary_key_from_instance(other),
1092                    )
1093                ]
1094            )
1095
1096            return ~self._criterion_exists(criterion)
1097
1098        def __ne__(self, other: Any) -> ColumnElement[bool]:  # type: ignore[override]  # noqa: E501
1099            """Implement the ``!=`` operator.
1100
1101            In a many-to-one context, such as:
1102
1103            .. sourcecode:: text
1104
1105              MyClass.some_prop != <some object>
1106
1107            This will typically produce a clause such as:
1108
1109            .. sourcecode:: sql
1110
1111              mytable.related_id != <some id>
1112
1113            Where ``<some id>`` is the primary key of the
1114            given object.
1115
1116            The ``!=`` operator provides partial functionality for non-
1117            many-to-one comparisons:
1118
1119            * Comparisons against collections are not supported.
1120              Use
1121              :meth:`~.RelationshipProperty.Comparator.contains`
1122              in conjunction with :func:`_expression.not_`.
1123            * Compared to a scalar one-to-many, will produce a
1124              clause that compares the target columns in the parent to
1125              the given target.
1126            * Compared to a scalar many-to-many, an alias
1127              of the association table will be rendered as
1128              well, forming a natural join that is part of the
1129              main body of the query. This will not work for
1130              queries that go beyond simple AND conjunctions of
1131              comparisons, such as those which use OR. Use
1132              explicit joins, outerjoins, or
1133              :meth:`~.RelationshipProperty.Comparator.has` in
1134              conjunction with :func:`_expression.not_` for
1135              more comprehensive non-many-to-one scalar
1136              membership tests.
1137            * Comparisons against ``None`` given in a one-to-many
1138              or many-to-many context produce an EXISTS clause.
1139
1140            """
1141            if other is None or isinstance(other, expression.Null):
1142                if self.property.direction == MANYTOONE:
1143                    return _orm_annotate(
1144                        ~self.property._optimized_compare(
1145                            None, adapt_source=self.adapter
1146                        )
1147                    )
1148
1149                else:
1150                    return self._criterion_exists()
1151            elif self.property.uselist:
1152                raise sa_exc.InvalidRequestError(
1153                    "Can't compare a collection"
1154                    " to an object or collection; use "
1155                    "contains() to test for membership."
1156                )
1157            else:
1158                return _orm_annotate(self.__negated_contains_or_equals(other))
1159
1160        def _memoized_attr_property(self) -> RelationshipProperty[_PT]:
1161            self.prop.parent._check_configure()
1162            return self.prop
1163
1164    def _with_parent(
1165        self,
1166        instance: object,
1167        alias_secondary: bool = True,
1168        from_entity: Optional[_EntityType[Any]] = None,
1169    ) -> ColumnElement[bool]:
1170        assert instance is not None
1171        adapt_source: Optional[_CoreAdapterProto] = None
1172        if from_entity is not None:
1173            insp: Optional[_InternalEntityType[Any]] = inspect(from_entity)
1174            assert insp is not None
1175            if insp_is_aliased_class(insp):
1176                adapt_source = insp._adapter.adapt_clause
1177        return self._optimized_compare(
1178            instance,
1179            value_is_parent=True,
1180            adapt_source=adapt_source,
1181            alias_secondary=alias_secondary,
1182        )
1183
1184    def _optimized_compare(
1185        self,
1186        state: Any,
1187        value_is_parent: bool = False,
1188        adapt_source: Optional[_CoreAdapterProto] = None,
1189        alias_secondary: bool = True,
1190    ) -> ColumnElement[bool]:
1191        if state is not None:
1192            try:
1193                state = inspect(state)
1194            except sa_exc.NoInspectionAvailable:
1195                state = None
1196
1197            if state is None or not getattr(state, "is_instance", False):
1198                raise sa_exc.ArgumentError(
1199                    "Mapped instance expected for relationship "
1200                    "comparison to object.   Classes, queries and other "

Showing the first 1,200 of 3509 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai