1# orm/relationships.py
2# Copyright (C) 2005-2021 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 absolute_import
17
18import collections
19import re
20import weakref
21
22from . import attributes
23from .base import _is_mapped_class
24from .base import state_str
25from .interfaces import MANYTOMANY
26from .interfaces import MANYTOONE
27from .interfaces import ONETOMANY
28from .interfaces import PropComparator
29from .interfaces import StrategizedProperty
30from .util import _orm_annotate
31from .util import _orm_deannotate
32from .util import CascadeOptions
33from .. import exc as sa_exc
34from .. import log
35from .. import schema
36from .. import sql
37from .. import util
38from ..inspection import inspect
39from ..sql import coercions
40from ..sql import expression
41from ..sql import operators
42from ..sql import roles
43from ..sql import visitors
44from ..sql.util import _deep_deannotate
45from ..sql.util import _shallow_annotate
46from ..sql.util import adapt_criterion_to_null
47from ..sql.util import ClauseAdapter
48from ..sql.util import join_condition
49from ..sql.util import selectables_overlap
50from ..sql.util import visit_binary_product
51
52
53def remote(expr):
54    """Annotate a portion of a primaryjoin expression
55    with a 'remote' annotation.
56
57    See the section :ref:`relationship_custom_foreign` for a
58    description of use.
59
60    .. seealso::
61
62        :ref:`relationship_custom_foreign`
63
64        :func:`.foreign`
65
66    """
67    return _annotate_columns(
68        coercions.expect(roles.ColumnArgumentRole, expr), {"remote": True}
69    )
70
71
72def foreign(expr):
73    """Annotate a portion of a primaryjoin expression
74    with a 'foreign' annotation.
75
76    See the section :ref:`relationship_custom_foreign` for a
77    description of use.
78
79    .. seealso::
80
81        :ref:`relationship_custom_foreign`
82
83        :func:`.remote`
84
85    """
86
87    return _annotate_columns(
88        coercions.expect(roles.ColumnArgumentRole, expr), {"foreign": True}
89    )
90
91
92@log.class_logger
93class RelationshipProperty(StrategizedProperty):
94    """Describes an object property that holds a single item or list
95    of items that correspond to a related database table.
96
97    Public constructor is the :func:`_orm.relationship` function.
98
99    .. seealso::
100
101        :ref:`relationship_config_toplevel`
102
103    """
104
105    strategy_wildcard_key = "relationship"
106    inherit_cache = True
107
108    _links_to_entity = True
109
110    _persistence_only = dict(
111        passive_deletes=False,
112        passive_updates=True,
113        enable_typechecks=True,
114        active_history=False,
115        cascade_backrefs=True,
116    )
117
118    _dependency_processor = None
119
120    def __init__(
121        self,
122        argument,
123        secondary=None,
124        primaryjoin=None,
125        secondaryjoin=None,
126        foreign_keys=None,
127        uselist=None,
128        order_by=False,
129        backref=None,
130        back_populates=None,
131        overlaps=None,
132        post_update=False,
133        cascade=False,
134        viewonly=False,
135        lazy="select",
136        collection_class=None,
137        passive_deletes=_persistence_only["passive_deletes"],
138        passive_updates=_persistence_only["passive_updates"],
139        remote_side=None,
140        enable_typechecks=_persistence_only["enable_typechecks"],
141        join_depth=None,
142        comparator_factory=None,
143        single_parent=False,
144        innerjoin=False,
145        distinct_target_key=None,
146        doc=None,
147        active_history=_persistence_only["active_history"],
148        cascade_backrefs=_persistence_only["cascade_backrefs"],
149        load_on_pending=False,
150        bake_queries=True,
151        _local_remote_pairs=None,
152        query_class=None,
153        info=None,
154        omit_join=None,
155        sync_backref=None,
156        _legacy_inactive_history_style=False,
157    ):
158        """Provide a relationship between two mapped classes.
159
160        This corresponds to a parent-child or associative table relationship.
161        The constructed class is an instance of
162        :class:`.RelationshipProperty`.
163
164        A typical :func:`_orm.relationship`, used in a classical mapping::
165
166           mapper(Parent, properties={
167             'children': relationship(Child)
168           })
169
170        Some arguments accepted by :func:`_orm.relationship`
171        optionally accept a
172        callable function, which when called produces the desired value.
173        The callable is invoked by the parent :class:`_orm.Mapper` at "mapper
174        initialization" time, which happens only when mappers are first used,
175        and is assumed to be after all mappings have been constructed.  This
176        can be used to resolve order-of-declaration and other dependency
177        issues, such as if ``Child`` is declared below ``Parent`` in the same
178        file::
179
180            mapper(Parent, properties={
181                "children":relationship(lambda: Child,
182                                    order_by=lambda: Child.id)
183            })
184
185        When using the :ref:`declarative_toplevel` extension, the Declarative
186        initializer allows string arguments to be passed to
187        :func:`_orm.relationship`.  These string arguments are converted into
188        callables that evaluate the string as Python code, using the
189        Declarative class-registry as a namespace.  This allows the lookup of
190        related classes to be automatic via their string name, and removes the
191        need for related classes to be imported into the local module space
192        before the dependent classes have been declared.  It is still required
193        that the modules in which these related classes appear are imported
194        anywhere in the application at some point before the related mappings
195        are actually used, else a lookup error will be raised when the
196        :func:`_orm.relationship`
197        attempts to resolve the string reference to the
198        related class.    An example of a string- resolved class is as
199        follows::
200
201            from sqlalchemy.ext.declarative import declarative_base
202
203            Base = declarative_base()
204
205            class Parent(Base):
206                __tablename__ = 'parent'
207                id = Column(Integer, primary_key=True)
208                children = relationship("Child", order_by="Child.id")
209
210        .. seealso::
211
212          :ref:`relationship_config_toplevel` - Full introductory and
213          reference documentation for :func:`_orm.relationship`.
214
215          :ref:`orm_tutorial_relationship` - ORM tutorial introduction.
216
217        :param argument:
218          A mapped class, or actual :class:`_orm.Mapper` instance,
219          representing
220          the target of the relationship.
221
222          :paramref:`_orm.relationship.argument`
223          may also be passed as a callable
224          function which is evaluated at mapper initialization time, and may
225          be passed as a string name when using Declarative.
226
227          .. warning:: Prior to SQLAlchemy 1.3.16, this value is interpreted
228             using Python's ``eval()`` function.
229             **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
230             See :ref:`declarative_relationship_eval` for details on
231             declarative evaluation of :func:`_orm.relationship` arguments.
232
233          .. versionchanged 1.3.16::
234
235             The string evaluation of the main "argument" no longer accepts an
236             open ended Python expression, instead only accepting a string
237             class name or dotted package-qualified name.
238
239          .. seealso::
240
241            :ref:`declarative_configuring_relationships` - further detail
242            on relationship configuration when using Declarative.
243
244        :param secondary:
245          For a many-to-many relationship, specifies the intermediary
246          table, and is typically an instance of :class:`_schema.Table`.
247          In less common circumstances, the argument may also be specified
248          as an :class:`_expression.Alias` construct, or even a
249          :class:`_expression.Join` construct.
250
251          :paramref:`_orm.relationship.secondary` may
252          also be passed as a callable function which is evaluated at
253          mapper initialization time.  When using Declarative, it may also
254          be a string argument noting the name of a :class:`_schema.Table`
255          that is
256          present in the :class:`_schema.MetaData`
257          collection associated with the
258          parent-mapped :class:`_schema.Table`.
259
260          .. warning:: When passed as a Python-evaluable string, the
261             argument is interpreted using Python's ``eval()`` function.
262             **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
263             See :ref:`declarative_relationship_eval` for details on
264             declarative evaluation of :func:`_orm.relationship` arguments.
265
266          The :paramref:`_orm.relationship.secondary` keyword argument is
267          typically applied in the case where the intermediary
268          :class:`_schema.Table`
269          is not otherwise expressed in any direct class mapping. If the
270          "secondary" table is also explicitly mapped elsewhere (e.g. as in
271          :ref:`association_pattern`), one should consider applying the
272          :paramref:`_orm.relationship.viewonly` flag so that this
273          :func:`_orm.relationship`
274          is not used for persistence operations which
275          may conflict with those of the association object pattern.
276
277          .. seealso::
278
279              :ref:`relationships_many_to_many` - Reference example of "many
280              to many".
281
282              :ref:`orm_tutorial_many_to_many` - ORM tutorial introduction to
283              many-to-many relationships.
284
285              :ref:`self_referential_many_to_many` - Specifics on using
286              many-to-many in a self-referential case.
287
288              :ref:`declarative_many_to_many` - Additional options when using
289              Declarative.
290
291              :ref:`association_pattern` - an alternative to
292              :paramref:`_orm.relationship.secondary`
293              when composing association
294              table relationships, allowing additional attributes to be
295              specified on the association table.
296
297              :ref:`composite_secondary_join` - a lesser-used pattern which
298              in some cases can enable complex :func:`_orm.relationship` SQL
299              conditions to be used.
300
301          .. versionadded:: 0.9.2 :paramref:`_orm.relationship.secondary`
302             works
303             more effectively when referring to a :class:`_expression.Join`
304             instance.
305
306        :param active_history=False:
307          When ``True``, indicates that the "previous" value for a
308          many-to-one reference should be loaded when replaced, if
309          not already loaded. Normally, history tracking logic for
310          simple many-to-ones only needs to be aware of the "new"
311          value in order to perform a flush. This flag is available
312          for applications that make use of
313          :func:`.attributes.get_history` which also need to know
314          the "previous" value of the attribute.
315
316        :param backref:
317          Indicates the string name of a property to be placed on the related
318          mapper's class that will handle this relationship in the other
319          direction. The other property will be created automatically
320          when the mappers are configured.  Can also be passed as a
321          :func:`.backref` object to control the configuration of the
322          new relationship.
323
324          .. seealso::
325
326            :ref:`relationships_backref` - Introductory documentation and
327            examples.
328
329            :paramref:`_orm.relationship.back_populates` - alternative form
330            of backref specification.
331
332            :func:`.backref` - allows control over :func:`_orm.relationship`
333            configuration when using :paramref:`_orm.relationship.backref`.
334
335
336        :param back_populates:
337          Takes a string name and has the same meaning as
338          :paramref:`_orm.relationship.backref`, except the complementing
339          property is **not** created automatically, and instead must be
340          configured explicitly on the other mapper.  The complementing
341          property should also indicate
342          :paramref:`_orm.relationship.back_populates` to this relationship to
343          ensure proper functioning.
344
345          .. seealso::
346
347            :ref:`relationships_backref` - Introductory documentation and
348            examples.
349
350            :paramref:`_orm.relationship.backref` - alternative form
351            of backref specification.
352
353        :param overlaps:
354           A string name or comma-delimited set of names of other relationships
355           on either this mapper, a descendant mapper, or a target mapper with
356           which this relationship may write to the same foreign keys upon
357           persistence.   The only effect this has is to eliminate the
358           warning that this relationship will conflict with another upon
359           persistence.   This is used for such relationships that are truly
360           capable of conflicting with each other on write, but the application
361           will ensure that no such conflicts occur.
362
363           .. versionadded:: 1.4
364
365           .. seealso::
366
367                :ref:`error_qzyx` - usage example
368
369        :param bake_queries=True:
370          Enable :ref:`lambda caching <engine_lambda_caching>` for loader
371          strategies, if applicable, which adds a performance gain to the
372          construction of SQL constructs used by loader strategies, in addition
373          to the usual SQL statement caching used throughout SQLAlchemy. This
374          parameter currently applies only to the "lazy" and "selectin" loader
375          strategies. There is generally no reason to set this parameter to
376          False.
377
378          .. versionchanged:: 1.4  Relationship loaders no longer use the
379             previous "baked query" system of query caching.   The "lazy"
380             and "selectin" loaders make use of the "lambda cache" system
381             for the construction of SQL constructs,
382             as well as the usual SQL caching system that is throughout
383             SQLAlchemy as of the 1.4 series.
384
385        :param cascade:
386          A comma-separated list of cascade rules which determines how
387          Session operations should be "cascaded" from parent to child.
388          This defaults to ``False``, which means the default cascade
389          should be used - this default cascade is ``"save-update, merge"``.
390
391          The available cascades are ``save-update``, ``merge``,
392          ``expunge``, ``delete``, ``delete-orphan``, and ``refresh-expire``.
393          An additional option, ``all`` indicates shorthand for
394          ``"save-update, merge, refresh-expire,
395          expunge, delete"``, and is often used as in ``"all, delete-orphan"``
396          to indicate that related objects should follow along with the
397          parent object in all cases, and be deleted when de-associated.
398
399          .. seealso::
400
401            :ref:`unitofwork_cascades` - Full detail on each of the available
402            cascade options.
403
404            :ref:`tutorial_delete_cascade` - Tutorial example describing
405            a delete cascade.
406
407        :param cascade_backrefs=True:
408          A boolean value indicating if the ``save-update`` cascade should
409          operate along an assignment event intercepted by a backref.
410          When set to ``False``, the attribute managed by this relationship
411          will not cascade an incoming transient object into the session of a
412          persistent parent, if the event is received via backref.
413
414          .. deprecated:: 1.4 The
415             :paramref:`_orm.relationship.cascade_backrefs`
416             flag will default to False in all cases in SQLAlchemy 2.0.
417
418          .. seealso::
419
420            :ref:`backref_cascade` - Full discussion and examples on how
421            the :paramref:`_orm.relationship.cascade_backrefs` option is used.
422
423        :param collection_class:
424          A class or callable that returns a new list-holding object. will
425          be used in place of a plain list for storing elements.
426
427          .. seealso::
428
429            :ref:`custom_collections` - Introductory documentation and
430            examples.
431
432        :param comparator_factory:
433          A class which extends :class:`.RelationshipProperty.Comparator`
434          which provides custom SQL clause generation for comparison
435          operations.
436
437          .. seealso::
438
439            :class:`.PropComparator` - some detail on redefining comparators
440            at this level.
441
442            :ref:`custom_comparators` - Brief intro to this feature.
443
444
445        :param distinct_target_key=None:
446          Indicate if a "subquery" eager load should apply the DISTINCT
447          keyword to the innermost SELECT statement.  When left as ``None``,
448          the DISTINCT keyword will be applied in those cases when the target
449          columns do not comprise the full primary key of the target table.
450          When set to ``True``, the DISTINCT keyword is applied to the
451          innermost SELECT unconditionally.
452
453          It may be desirable to set this flag to False when the DISTINCT is
454          reducing performance of the innermost subquery beyond that of what
455          duplicate innermost rows may be causing.
456
457          .. versionchanged:: 0.9.0 -
458             :paramref:`_orm.relationship.distinct_target_key` now defaults to
459             ``None``, so that the feature enables itself automatically for
460             those cases where the innermost query targets a non-unique
461             key.
462
463          .. seealso::
464
465            :ref:`loading_toplevel` - includes an introduction to subquery
466            eager loading.
467
468        :param doc:
469          Docstring which will be applied to the resulting descriptor.
470
471        :param foreign_keys:
472
473          A list of columns which are to be used as "foreign key"
474          columns, or columns which refer to the value in a remote
475          column, within the context of this :func:`_orm.relationship`
476          object's :paramref:`_orm.relationship.primaryjoin` condition.
477          That is, if the :paramref:`_orm.relationship.primaryjoin`
478          condition of this :func:`_orm.relationship` is ``a.id ==
479          b.a_id``, and the values in ``b.a_id`` are required to be
480          present in ``a.id``, then the "foreign key" column of this
481          :func:`_orm.relationship` is ``b.a_id``.
482
483          In normal cases, the :paramref:`_orm.relationship.foreign_keys`
484          parameter is **not required.** :func:`_orm.relationship` will
485          automatically determine which columns in the
486          :paramref:`_orm.relationship.primaryjoin` condition are to be
487          considered "foreign key" columns based on those
488          :class:`_schema.Column` objects that specify
489          :class:`_schema.ForeignKey`,
490          or are otherwise listed as referencing columns in a
491          :class:`_schema.ForeignKeyConstraint` construct.
492          :paramref:`_orm.relationship.foreign_keys` is only needed when:
493
494            1. There is more than one way to construct a join from the local
495               table to the remote table, as there are multiple foreign key
496               references present.  Setting ``foreign_keys`` will limit the
497               :func:`_orm.relationship`
498               to consider just those columns specified
499               here as "foreign".
500
501            2. The :class:`_schema.Table` being mapped does not actually have
502               :class:`_schema.ForeignKey` or
503               :class:`_schema.ForeignKeyConstraint`
504               constructs present, often because the table
505               was reflected from a database that does not support foreign key
506               reflection (MySQL MyISAM).
507
508            3. The :paramref:`_orm.relationship.primaryjoin`
509               argument is used to
510               construct a non-standard join condition, which makes use of
511               columns or expressions that do not normally refer to their
512               "parent" column, such as a join condition expressed by a
513               complex comparison using a SQL function.
514
515          The :func:`_orm.relationship` construct will raise informative
516          error messages that suggest the use of the
517          :paramref:`_orm.relationship.foreign_keys` parameter when
518          presented with an ambiguous condition.   In typical cases,
519          if :func:`_orm.relationship` doesn't raise any exceptions, the
520          :paramref:`_orm.relationship.foreign_keys` parameter is usually
521          not needed.
522
523          :paramref:`_orm.relationship.foreign_keys` may also be passed as a
524          callable function which is evaluated at mapper initialization time,
525          and may be passed as a Python-evaluable string when using
526          Declarative.
527
528          .. warning:: When passed as a Python-evaluable string, the
529             argument is interpreted using Python's ``eval()`` function.
530             **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
531             See :ref:`declarative_relationship_eval` for details on
532             declarative evaluation of :func:`_orm.relationship` arguments.
533
534          .. seealso::
535
536            :ref:`relationship_foreign_keys`
537
538            :ref:`relationship_custom_foreign`
539
540            :func:`.foreign` - allows direct annotation of the "foreign"
541            columns within a :paramref:`_orm.relationship.primaryjoin`
542            condition.
543
544        :param info: Optional data dictionary which will be populated into the
545            :attr:`.MapperProperty.info` attribute of this object.
546
547        :param innerjoin=False:
548          When ``True``, joined eager loads will use an inner join to join
549          against related tables instead of an outer join.  The purpose
550          of this option is generally one of performance, as inner joins
551          generally perform better than outer joins.
552
553          This flag can be set to ``True`` when the relationship references an
554          object via many-to-one using local foreign keys that are not
555          nullable, or when the reference is one-to-one or a collection that
556          is guaranteed to have one or at least one entry.
557
558          The option supports the same "nested" and "unnested" options as
559          that of :paramref:`_orm.joinedload.innerjoin`.  See that flag
560          for details on nested / unnested behaviors.
561
562          .. seealso::
563
564            :paramref:`_orm.joinedload.innerjoin` - the option as specified by
565            loader option, including detail on nesting behavior.
566
567            :ref:`what_kind_of_loading` - Discussion of some details of
568            various loader options.
569
570
571        :param join_depth:
572          When non-``None``, an integer value indicating how many levels
573          deep "eager" loaders should join on a self-referring or cyclical
574          relationship.  The number counts how many times the same Mapper
575          shall be present in the loading condition along a particular join
576          branch.  When left at its default of ``None``, eager loaders
577          will stop chaining when they encounter a the same target mapper
578          which is already higher up in the chain.  This option applies
579          both to joined- and subquery- eager loaders.
580
581          .. seealso::
582
583            :ref:`self_referential_eager_loading` - Introductory documentation
584            and examples.
585
586        :param lazy='select': specifies
587          How the related items should be loaded.  Default value is
588          ``select``.  Values include:
589
590          * ``select`` - items should be loaded lazily when the property is
591            first accessed, using a separate SELECT statement, or identity map
592            fetch for simple many-to-one references.
593
594          * ``immediate`` - items should be loaded as the parents are loaded,
595            using a separate SELECT statement, or identity map fetch for
596            simple many-to-one references.
597
598          * ``joined`` - items should be loaded "eagerly" in the same query as
599            that of the parent, using a JOIN or LEFT OUTER JOIN.  Whether
600            the join is "outer" or not is determined by the
601            :paramref:`_orm.relationship.innerjoin` parameter.
602
603          * ``subquery`` - items should be loaded "eagerly" as the parents are
604            loaded, using one additional SQL statement, which issues a JOIN to
605            a subquery of the original statement, for each collection
606            requested.
607
608          * ``selectin`` - items should be loaded "eagerly" as the parents
609            are loaded, using one or more additional SQL statements, which
610            issues a JOIN to the immediate parent object, specifying primary
611            key identifiers using an IN clause.
612
613            .. versionadded:: 1.2
614
615          * ``noload`` - no loading should occur at any time.  This is to
616            support "write-only" attributes, or attributes which are
617            populated in some manner specific to the application.
618
619          * ``raise`` - lazy loading is disallowed; accessing
620            the attribute, if its value were not already loaded via eager
621            loading, will raise an :exc:`~sqlalchemy.exc.InvalidRequestError`.
622            This strategy can be used when objects are to be detached from
623            their attached :class:`.Session` after they are loaded.
624
625            .. versionadded:: 1.1
626
627          * ``raise_on_sql`` - lazy loading that emits SQL is disallowed;
628            accessing the attribute, if its value were not already loaded via
629            eager loading, will raise an
630            :exc:`~sqlalchemy.exc.InvalidRequestError`, **if the lazy load
631            needs to emit SQL**.  If the lazy load can pull the related value
632            from the identity map or determine that it should be None, the
633            value is loaded.  This strategy can be used when objects will
634            remain associated with the attached :class:`.Session`, however
635            additional SELECT statements should be blocked.
636
637            .. versionadded:: 1.1
638
639          * ``dynamic`` - the attribute will return a pre-configured
640            :class:`_query.Query` object for all read
641            operations, onto which further filtering operations can be
642            applied before iterating the results.  See
643            the section :ref:`dynamic_relationship` for more details.
644
645          * True - a synonym for 'select'
646
647          * False - a synonym for 'joined'
648
649          * None - a synonym for 'noload'
650
651          .. seealso::
652
653            :doc:`/orm/loading_relationships` - Full documentation on
654            relationship loader configuration.
655
656            :ref:`dynamic_relationship` - detail on the ``dynamic`` option.
657
658            :ref:`collections_noload_raiseload` - notes on "noload" and "raise"
659
660        :param load_on_pending=False:
661          Indicates loading behavior for transient or pending parent objects.
662
663          When set to ``True``, causes the lazy-loader to
664          issue a query for a parent object that is not persistent, meaning it
665          has never been flushed.  This may take effect for a pending object
666          when autoflush is disabled, or for a transient object that has been
667          "attached" to a :class:`.Session` but is not part of its pending
668          collection.
669
670          The :paramref:`_orm.relationship.load_on_pending`
671          flag does not improve
672          behavior when the ORM is used normally - object references should be
673          constructed at the object level, not at the foreign key level, so
674          that they are present in an ordinary way before a flush proceeds.
675          This flag is not not intended for general use.
676
677          .. seealso::
678
679              :meth:`.Session.enable_relationship_loading` - this method
680              establishes "load on pending" behavior for the whole object, and
681              also allows loading on objects that remain transient or
682              detached.
683
684        :param order_by:
685          Indicates the ordering that should be applied when loading these
686          items.  :paramref:`_orm.relationship.order_by`
687          is expected to refer to
688          one of the :class:`_schema.Column`
689          objects to which the target class is
690          mapped, or the attribute itself bound to the target class which
691          refers to the column.
692
693          :paramref:`_orm.relationship.order_by`
694          may also be passed as a callable
695          function which is evaluated at mapper initialization time, and may
696          be passed as a Python-evaluable string when using Declarative.
697
698          .. warning:: When passed as a Python-evaluable string, the
699             argument is interpreted using Python's ``eval()`` function.
700             **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
701             See :ref:`declarative_relationship_eval` for details on
702             declarative evaluation of :func:`_orm.relationship` arguments.
703
704        :param passive_deletes=False:
705           Indicates loading behavior during delete operations.
706
707           A value of True indicates that unloaded child items should not
708           be loaded during a delete operation on the parent.  Normally,
709           when a parent item is deleted, all child items are loaded so
710           that they can either be marked as deleted, or have their
711           foreign key to the parent set to NULL.  Marking this flag as
712           True usually implies an ON DELETE <CASCADE|SET NULL> rule is in
713           place which will handle updating/deleting child rows on the
714           database side.
715
716           Additionally, setting the flag to the string value 'all' will
717           disable the "nulling out" of the child foreign keys, when the parent
718           object is deleted and there is no delete or delete-orphan cascade
719           enabled.  This is typically used when a triggering or error raise
720           scenario is in place on the database side.  Note that the foreign
721           key attributes on in-session child objects will not be changed after
722           a flush occurs so this is a very special use-case setting.
723           Additionally, the "nulling out" will still occur if the child
724           object is de-associated with the parent.
725
726           .. seealso::
727
728                :ref:`passive_deletes` - Introductory documentation
729                and examples.
730
731        :param passive_updates=True:
732          Indicates the persistence behavior to take when a referenced
733          primary key value changes in place, indicating that the referencing
734          foreign key columns will also need their value changed.
735
736          When True, it is assumed that ``ON UPDATE CASCADE`` is configured on
737          the foreign key in the database, and that the database will
738          handle propagation of an UPDATE from a source column to
739          dependent rows.  When False, the SQLAlchemy
740          :func:`_orm.relationship`
741          construct will attempt to emit its own UPDATE statements to
742          modify related targets.  However note that SQLAlchemy **cannot**
743          emit an UPDATE for more than one level of cascade.  Also,
744          setting this flag to False is not compatible in the case where
745          the database is in fact enforcing referential integrity, unless
746          those constraints are explicitly "deferred", if the target backend
747          supports it.
748
749          It is highly advised that an application which is employing
750          mutable primary keys keeps ``passive_updates`` set to True,
751          and instead uses the referential integrity features of the database
752          itself in order to handle the change efficiently and fully.
753
754          .. seealso::
755
756              :ref:`passive_updates` - Introductory documentation and
757              examples.
758
759              :paramref:`.mapper.passive_updates` - a similar flag which
760              takes effect for joined-table inheritance mappings.
761
762        :param post_update:
763          This indicates that the relationship should be handled by a
764          second UPDATE statement after an INSERT or before a
765          DELETE. Currently, it also will issue an UPDATE after the
766          instance was UPDATEd as well, although this technically should
767          be improved. This flag is used to handle saving bi-directional
768          dependencies between two individual rows (i.e. each row
769          references the other), where it would otherwise be impossible to
770          INSERT or DELETE both rows fully since one row exists before the
771          other. Use this flag when a particular mapping arrangement will
772          incur two rows that are dependent on each other, such as a table
773          that has a one-to-many relationship to a set of child rows, and
774          also has a column that references a single child row within that
775          list (i.e. both tables contain a foreign key to each other). If
776          a flush operation returns an error that a "cyclical
777          dependency" was detected, this is a cue that you might want to
778          use :paramref:`_orm.relationship.post_update` to "break" the cycle.
779
780          .. seealso::
781
782              :ref:`post_update` - Introductory documentation and examples.
783
784        :param primaryjoin:
785          A SQL expression that will be used as the primary
786          join of the child object against the parent object, or in a
787          many-to-many relationship the join of the parent object to the
788          association table. By default, this value is computed based on the
789          foreign key relationships of the parent and child tables (or
790          association table).
791
792          :paramref:`_orm.relationship.primaryjoin` may also be passed as a
793          callable function which is evaluated at mapper initialization time,
794          and may be passed as a Python-evaluable string when using
795          Declarative.
796
797          .. warning:: When passed as a Python-evaluable string, the
798             argument is interpreted using Python's ``eval()`` function.
799             **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
800             See :ref:`declarative_relationship_eval` for details on
801             declarative evaluation of :func:`_orm.relationship` arguments.
802
803          .. seealso::
804
805              :ref:`relationship_primaryjoin`
806
807        :param remote_side:
808          Used for self-referential relationships, indicates the column or
809          list of columns that form the "remote side" of the relationship.
810
811          :paramref:`_orm.relationship.remote_side` may also be passed as a
812          callable function which is evaluated at mapper initialization time,
813          and may be passed as a Python-evaluable string when using
814          Declarative.
815
816          .. warning:: When passed as a Python-evaluable string, the
817             argument is interpreted using Python's ``eval()`` function.
818             **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
819             See :ref:`declarative_relationship_eval` for details on
820             declarative evaluation of :func:`_orm.relationship` arguments.
821
822          .. seealso::
823
824            :ref:`self_referential` - in-depth explanation of how
825            :paramref:`_orm.relationship.remote_side`
826            is used to configure self-referential relationships.
827
828            :func:`.remote` - an annotation function that accomplishes the
829            same purpose as :paramref:`_orm.relationship.remote_side`,
830            typically
831            when a custom :paramref:`_orm.relationship.primaryjoin` condition
832            is used.
833
834        :param query_class:
835          A :class:`_query.Query`
836          subclass that will be used internally by the
837          ``AppenderQuery`` returned by a "dynamic" relationship, that
838          is, a relationship that specifies ``lazy="dynamic"`` or was
839          otherwise constructed using the :func:`_orm.dynamic_loader`
840          function.
841
842          .. seealso::
843
844            :ref:`dynamic_relationship` - Introduction to "dynamic"
845            relationship loaders.
846
847        :param secondaryjoin:
848          A SQL expression that will be used as the join of
849          an association table to the child object. By default, this value is
850          computed based on the foreign key relationships of the association
851          and child tables.
852
853          :paramref:`_orm.relationship.secondaryjoin` may also be passed as a
854          callable function which is evaluated at mapper initialization time,
855          and may be passed as a Python-evaluable string when using
856          Declarative.
857
858          .. warning:: When passed as a Python-evaluable string, the
859             argument is interpreted using Python's ``eval()`` function.
860             **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
861             See :ref:`declarative_relationship_eval` for details on
862             declarative evaluation of :func:`_orm.relationship` arguments.
863
864          .. seealso::
865
866              :ref:`relationship_primaryjoin`
867
868        :param single_parent:
869          When True, installs a validator which will prevent objects
870          from being associated with more than one parent at a time.
871          This is used for many-to-one or many-to-many relationships that
872          should be treated either as one-to-one or one-to-many.  Its usage
873          is optional, except for :func:`_orm.relationship` constructs which
874          are many-to-one or many-to-many and also
875          specify the ``delete-orphan`` cascade option.  The
876          :func:`_orm.relationship` construct itself will raise an error
877          instructing when this option is required.
878
879          .. seealso::
880
881            :ref:`unitofwork_cascades` - includes detail on when the
882            :paramref:`_orm.relationship.single_parent`
883            flag may be appropriate.
884
885        :param uselist:
886          A boolean that indicates if this property should be loaded as a
887          list or a scalar. In most cases, this value is determined
888          automatically by :func:`_orm.relationship` at mapper configuration
889          time, based on the type and direction
890          of the relationship - one to many forms a list, many to one
891          forms a scalar, many to many is a list. If a scalar is desired
892          where normally a list would be present, such as a bi-directional
893          one-to-one relationship, set :paramref:`_orm.relationship.uselist`
894          to
895          False.
896
897          The :paramref:`_orm.relationship.uselist`
898          flag is also available on an
899          existing :func:`_orm.relationship`
900          construct as a read-only attribute,
901          which can be used to determine if this :func:`_orm.relationship`
902          deals
903          with collections or scalar attributes::
904
905              >>> User.addresses.property.uselist
906              True
907
908          .. seealso::
909
910              :ref:`relationships_one_to_one` - Introduction to the "one to
911              one" relationship pattern, which is typically when the
912              :paramref:`_orm.relationship.uselist` flag is needed.
913
914        :param viewonly=False:
915          When set to ``True``, the relationship is used only for loading
916          objects, and not for any persistence operation.  A
917          :func:`_orm.relationship` which specifies
918          :paramref:`_orm.relationship.viewonly` can work
919          with a wider range of SQL operations within the
920          :paramref:`_orm.relationship.primaryjoin` condition, including
921          operations that feature the use of a variety of comparison operators
922          as well as SQL functions such as :func:`_expression.cast`.  The
923          :paramref:`_orm.relationship.viewonly`
924          flag is also of general use when defining any kind of
925          :func:`_orm.relationship` that doesn't represent
926          the full set of related objects, to prevent modifications of the
927          collection from resulting in persistence operations.
928
929          When using the :paramref:`_orm.relationship.viewonly` flag in
930          conjunction with backrefs, the originating relationship for a
931          particular state change will not produce state changes within the
932          viewonly relationship.   This is the behavior implied by
933          :paramref:`_orm.relationship.sync_backref` being set to False.
934
935          .. versionchanged:: 1.3.17 - the
936             :paramref:`_orm.relationship.sync_backref` flag is set to False
937                 when using viewonly in conjunction with backrefs.
938
939          .. seealso::
940
941            :paramref:`_orm.relationship.sync_backref`
942
943        :param sync_backref:
944          A boolean that enables the events used to synchronize the in-Python
945          attributes when this relationship is target of either
946          :paramref:`_orm.relationship.backref` or
947          :paramref:`_orm.relationship.back_populates`.
948
949          Defaults to ``None``, which indicates that an automatic value should
950          be selected based on the value of the
951          :paramref:`_orm.relationship.viewonly` flag.  When left at its
952          default, changes in state will be back-populated only if neither
953          sides of a relationship is viewonly.
954
955          .. versionadded:: 1.3.17
956
957          .. versionchanged:: 1.4 - A relationship that specifies
958             :paramref:`_orm.relationship.viewonly` automatically implies
959             that :paramref:`_orm.relationship.sync_backref` is ``False``.
960
961          .. seealso::
962
963            :paramref:`_orm.relationship.viewonly`
964
965        :param omit_join:
966          Allows manual control over the "selectin" automatic join
967          optimization.  Set to ``False`` to disable the "omit join" feature
968          added in SQLAlchemy 1.3; or leave as ``None`` to leave automatic
969          optimization in place.
970
971          .. note:: This flag may only be set to ``False``.   It is not
972             necessary to set it to ``True`` as the "omit_join" optimization is
973             automatically detected; if it is not detected, then the
974             optimization is not supported.
975
976             .. versionchanged:: 1.3.11  setting ``omit_join`` to True will now
977                emit a warning as this was not the intended use of this flag.
978
979          .. versionadded:: 1.3
980
981
982        """
983        super(RelationshipProperty, self).__init__()
984
985        self.uselist = uselist
986        self.argument = argument
987        self.secondary = secondary
988        self.primaryjoin = primaryjoin
989        self.secondaryjoin = secondaryjoin
990        self.post_update = post_update
991        self.direction = None
992        self.viewonly = viewonly
993        if viewonly:
994            self._warn_for_persistence_only_flags(
995                passive_deletes=passive_deletes,
996                passive_updates=passive_updates,
997                enable_typechecks=enable_typechecks,
998                active_history=active_history,
999                cascade_backrefs=cascade_backrefs,
1000            )
1001        if viewonly and sync_backref:
1002            raise sa_exc.ArgumentError(
1003                "sync_backref and viewonly cannot both be True"
1004            )
1005        self.sync_backref = sync_backref
1006        self.lazy = lazy
1007        self.single_parent = single_parent
1008        self._user_defined_foreign_keys = foreign_keys
1009        self.collection_class = collection_class
1010        self.passive_deletes = passive_deletes
1011        self.cascade_backrefs = cascade_backrefs
1012        self.passive_updates = passive_updates
1013        self.remote_side = remote_side
1014        self.enable_typechecks = enable_typechecks
1015        self.query_class = query_class
1016        self.innerjoin = innerjoin
1017        self.distinct_target_key = distinct_target_key
1018        self.doc = doc
1019        self.active_history = active_history
1020        self._legacy_inactive_history_style = _legacy_inactive_history_style
1021
1022        self.join_depth = join_depth
1023        if omit_join:
1024            util.warn(
1025                "setting omit_join to True is not supported; selectin "
1026                "loading of this relationship may not work correctly if this "
1027                "flag is set explicitly.  omit_join optimization is "
1028                "automatically detected for conditions under which it is "
1029                "supported."
1030            )
1031
1032        self.omit_join = omit_join
1033        self.local_remote_pairs = _local_remote_pairs
1034        self.bake_queries = bake_queries
1035        self.load_on_pending = load_on_pending
1036        self.comparator_factory = (
1037            comparator_factory or RelationshipProperty.Comparator
1038        )
1039        self.comparator = self.comparator_factory(self, None)
1040        util.set_creation_order(self)
1041
1042        if info is not None:
1043            self.info = info
1044
1045        self.strategy_key = (("lazy", self.lazy),)
1046
1047        self._reverse_property = set()
1048        if overlaps:
1049            self._overlaps = set(re.split(r"\s*,\s*", overlaps))
1050        else:
1051            self._overlaps = ()
1052
1053        if cascade is not False:
1054            self.cascade = cascade
1055        elif self.viewonly:
1056            self.cascade = "none"
1057        else:
1058            self.cascade = "save-update, merge"
1059
1060        self.order_by = order_by
1061
1062        self.back_populates = back_populates
1063
1064        if self.back_populates:
1065            if backref:
1066                raise sa_exc.ArgumentError(
1067                    "backref and back_populates keyword arguments "
1068                    "are mutually exclusive"
1069                )
1070            self.backref = None
1071        else:
1072            self.backref = backref
1073
1074    def _warn_for_persistence_only_flags(self, **kw):
1075        for k, v in kw.items():
1076            if v != self._persistence_only[k]:
1077                # we are warning here rather than warn deprecated as this is a
1078                # configuration mistake, and Python shows regular warnings more
1079                # aggressively than deprecation warnings by default. Unlike the
1080                # case of setting viewonly with cascade, the settings being
1081                # warned about here are not actively doing the wrong thing
1082                # against viewonly=True, so it is not as urgent to have these
1083                # raise an error.
1084                util.warn(
1085                    "Setting %s on relationship() while also "
1086                    "setting viewonly=True does not make sense, as a "
1087                    "viewonly=True relationship does not perform persistence "
1088                    "operations. This configuration may raise an error "
1089                    "in a future release." % (k,)
1090                )
1091
1092    def instrument_class(self, mapper):
1093        attributes.register_descriptor(
1094            mapper.class_,
1095            self.key,
1096            comparator=self.comparator_factory(self, mapper),
1097            parententity=mapper,
1098            doc=self.doc,
1099        )
1100
1101    class Comparator(PropComparator):
1102        """Produce boolean, comparison, and other operators for
1103        :class:`.RelationshipProperty` attributes.
1104
1105        See the documentation for :class:`.PropComparator` for a brief
1106        overview of ORM level operator definition.
1107
1108        .. seealso::
1109
1110            :class:`.PropComparator`
1111
1112            :class:`.ColumnProperty.Comparator`
1113
1114            :class:`.ColumnOperators`
1115
1116            :ref:`types_operators`
1117
1118            :attr:`.TypeEngine.comparator_factory`
1119
1120        """
1121
1122        _of_type = None
1123        _extra_criteria = ()
1124
1125        def __init__(
1126            self,
1127            prop,
1128            parentmapper,
1129            adapt_to_entity=None,
1130            of_type=None,
1131            extra_criteria=(),
1132        ):
1133            """Construction of :class:`.RelationshipProperty.Comparator`
1134            is internal to the ORM's attribute mechanics.
1135
1136            """
1137            self.prop = prop
1138            self._parententity = parentmapper
1139            self._adapt_to_entity = adapt_to_entity
1140            if of_type:
1141                self._of_type = of_type
1142            self._extra_criteria = extra_criteria
1143
1144        def adapt_to_entity(self, adapt_to_entity):
1145            return self.__class__(
1146                self.property,
1147                self._parententity,
1148                adapt_to_entity=adapt_to_entity,
1149                of_type=self._of_type,
1150            )
1151
1152        @util.memoized_property
1153        def entity(self):
1154            """The target entity referred to by this
1155            :class:`.RelationshipProperty.Comparator`.
1156
1157            This is either a :class:`_orm.Mapper` or :class:`.AliasedInsp`
1158            object.
1159
1160            This is the "target" or "remote" side of the
1161            :func:`_orm.relationship`.
1162
1163            """
1164            # this is a relatively recent change made for
1165            # 1.4.27 as part of #7244.
1166            # TODO: shouldn't _of_type be inspected up front when received?
1167            if self._of_type is not None:
1168                return inspect(self._of_type)
1169            else:
1170                return self.property.entity
1171
1172        @util.memoized_property
1173        def mapper(self):
1174            """The target :class:`_orm.Mapper` referred to by this
1175            :class:`.RelationshipProperty.Comparator`.
1176
1177            This is the "target" or "remote" side of the
1178            :func:`_orm.relationship`.
1179
1180            """
1181            return self.property.mapper
1182
1183        @util.memoized_property
1184        def _parententity(self):
1185            return self.property.parent
1186
1187        def _source_selectable(self):
1188            if self._adapt_to_entity:
1189                return self._adapt_to_entity.selectable
1190            else:
1191                return self.property.parent._with_polymorphic_selectable
1192
1193        def __clause_element__(self):
1194            adapt_from = self._source_selectable()
1195            if self._of_type:
1196                of_type_entity = inspect(self._of_type)
1197            else:
1198                of_type_entity = None
1199
1200            (
1201                pj,
1202                sj,
1203                source,
1204                dest,
1205                secondary,
1206                target_adapter,
1207            ) = self.property._create_joins(
1208                source_selectable=adapt_from,
1209                source_polymorphic=True,
1210                of_type_entity=of_type_entity,
1211                alias_secondary=True,
1212                extra_criteria=self._extra_criteria,
1213            )
1214            if sj is not None:
1215                return pj & sj
1216            else:
1217                return pj
1218
1219        def of_type(self, cls):
1220            r"""Redefine this object in terms of a polymorphic subclass.
1221
1222            See :meth:`.PropComparator.of_type` for an example.
1223
1224
1225            """
1226            return RelationshipProperty.Comparator(
1227                self.property,
1228                self._parententity,
1229                adapt_to_entity=self._adapt_to_entity,
1230                of_type=cls,
1231                extra_criteria=self._extra_criteria,
1232            )
1233
1234        def and_(self, *other):
1235            """Add AND criteria.
1236
1237            See :meth:`.PropComparator.and_` for an example.
1238
1239            .. versionadded:: 1.4
1240
1241            """
1242            return RelationshipProperty.Comparator(
1243                self.property,
1244                self._parententity,
1245                adapt_to_entity=self._adapt_to_entity,
1246                of_type=self._of_type,
1247                extra_criteria=self._extra_criteria + other,
1248            )
1249
1250        def in_(self, other):
1251            """Produce an IN clause - this is not implemented
1252            for :func:`_orm.relationship`-based attributes at this time.
1253
1254            """
1255            raise NotImplementedError(
1256                "in_() not yet supported for "
1257                "relationships.  For a simple "
1258                "many-to-one, use in_() against "
1259                "the set of foreign key values."
1260            )
1261
1262        __hash__ = None
1263
1264        def __eq__(self, other):
1265            """Implement the ``==`` operator.
1266
1267            In a many-to-one context, such as::
1268
1269              MyClass.some_prop == <some object>
1270
1271            this will typically produce a
1272            clause such as::
1273
1274              mytable.related_id == <some id>
1275
1276            Where ``<some id>`` is the primary key of the given
1277            object.
1278
1279            The ``==`` operator provides partial functionality for non-
1280            many-to-one comparisons:
1281
1282            * Comparisons against collections are not supported.
1283              Use :meth:`~.RelationshipProperty.Comparator.contains`.
1284            * Compared to a scalar one-to-many, will produce a
1285              clause that compares the target columns in the parent to
1286              the given target.
1287            * Compared to a scalar many-to-many, an alias
1288              of the association table will be rendered as
1289              well, forming a natural join that is part of the
1290              main body of the query. This will not work for
1291              queries that go beyond simple AND conjunctions of
1292              comparisons, such as those which use OR. Use
1293              explicit joins, outerjoins, or
1294              :meth:`~.RelationshipProperty.Comparator.has` for
1295              more comprehensive non-many-to-one scalar
1296              membership tests.
1297            * Comparisons against ``None`` given in a one-to-many
1298              or many-to-many context produce a NOT EXISTS clause.
1299
1300            """
1301            if isinstance(other, (util.NoneType, expression.Null)):
1302                if self.property.direction in [ONETOMANY, MANYTOMANY]:
1303                    return ~self._criterion_exists()
1304                else:
1305                    return _orm_annotate(
1306                        self.property._optimized_compare(
1307                            None, adapt_source=self.adapter
1308                        )
1309                    )
1310            elif self.property.uselist:
1311                raise sa_exc.InvalidRequestError(
1312                    "Can't compare a collection to an object or collection; "
1313                    "use contains() to test for membership."
1314                )
1315            else:
1316                return _orm_annotate(
1317                    self.property._optimized_compare(
1318                        other, adapt_source=self.adapter
1319                    )
1320                )
1321
1322        def _criterion_exists(self, criterion=None, **kwargs):
1323            if getattr(self, "_of_type", None):
1324                info = inspect(self._of_type)
1325                target_mapper, to_selectable, is_aliased_class = (
1326                    info.mapper,
1327                    info.selectable,
1328                    info.is_aliased_class,
1329                )
1330                if self.property._is_self_referential and not is_aliased_class:
1331                    to_selectable = to_selectable._anonymous_fromclause()
1332
1333                single_crit = target_mapper._single_table_criterion
1334                if single_crit is not None:
1335                    if criterion is not None:
1336                        criterion = single_crit & criterion
1337                    else:
1338                        criterion = single_crit
1339            else:
1340                is_aliased_class = False
1341                to_selectable = None
1342
1343            if self.adapter:
1344                source_selectable = self._source_selectable()
1345            else:
1346                source_selectable = None
1347
1348            (
1349                pj,
1350                sj,
1351                source,
1352                dest,
1353                secondary,
1354                target_adapter,
1355            ) = self.property._create_joins(
1356                dest_selectable=to_selectable,
1357                source_selectable=source_selectable,
1358            )
1359
1360            for k in kwargs:
1361                crit = getattr(self.property.mapper.class_, k) == kwargs[k]
1362                if criterion is None:
1363                    criterion = crit
1364                else:
1365                    criterion = criterion & crit
1366
1367            # annotate the *local* side of the join condition, in the case
1368            # of pj + sj this is the full primaryjoin, in the case of just
1369            # pj its the local side of the primaryjoin.
1370            if sj is not None:
1371                j = _orm_annotate(pj) & sj
1372            else:
1373                j = _orm_annotate(pj, exclude=self.property.remote_side)
1374
1375            if (
1376                criterion is not None
1377                and target_adapter
1378                and not is_aliased_class
1379            ):
1380                # limit this adapter to annotated only?
1381                criterion = target_adapter.traverse(criterion)
1382
1383            # only have the "joined left side" of what we
1384            # return be subject to Query adaption.  The right
1385            # side of it is used for an exists() subquery and
1386            # should not correlate or otherwise reach out
1387            # to anything in the enclosing query.
1388            if criterion is not None:
1389                criterion = criterion._annotate(
1390                    {"no_replacement_traverse": True}
1391                )
1392
1393            crit = j & sql.True_._ifnone(criterion)
1394
1395            if secondary is not None:
1396                ex = (
1397                    sql.exists(1)
1398                    .where(crit)
1399                    .select_from(dest, secondary)
1400                    .correlate_except(dest, secondary)
1401                )
1402            else:
1403                ex = (
1404                    sql.exists(1)
1405                    .where(crit)
1406                    .select_from(dest)
1407                    .correlate_except(dest)
1408                )
1409            return ex
1410
1411        def any(self, criterion=None, **kwargs):
1412            """Produce an expression that tests a collection against
1413            particular criterion, using EXISTS.
1414
1415            An expression like::
1416
1417                session.query(MyClass).filter(
1418                    MyClass.somereference.any(SomeRelated.x==2)
1419                )
1420
1421
1422            Will produce a query like::
1423
1424                SELECT * FROM my_table WHERE
1425                EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id
1426                AND related.x=2)
1427
1428            Because :meth:`~.RelationshipProperty.Comparator.any` uses
1429            a correlated subquery, its performance is not nearly as
1430            good when compared against large target tables as that of
1431            using a join.
1432
1433            :meth:`~.RelationshipProperty.Comparator.any` is particularly
1434            useful for testing for empty collections::
1435
1436                session.query(MyClass).filter(
1437                    ~MyClass.somereference.any()
1438                )
1439
1440            will produce::
1441
1442                SELECT * FROM my_table WHERE
1443                NOT (EXISTS (SELECT 1 FROM related WHERE
1444                related.my_id=my_table.id))
1445
1446            :meth:`~.RelationshipProperty.Comparator.any` is only
1447            valid for collections, i.e. a :func:`_orm.relationship`
1448            that has ``uselist=True``.  For scalar references,
1449            use :meth:`~.RelationshipProperty.Comparator.has`.
1450
1451            """
1452            if not self.property.uselist:
1453                raise sa_exc.InvalidRequestError(
1454                    "'any()' not implemented for scalar "
1455                    "attributes. Use has()."
1456                )
1457
1458            return self._criterion_exists(criterion, **kwargs)
1459
1460        def has(self, criterion=None, **kwargs):
1461            """Produce an expression that tests a scalar reference against
1462            particular criterion, using EXISTS.
1463
1464            An expression like::
1465
1466                session.query(MyClass).filter(
1467                    MyClass.somereference.has(SomeRelated.x==2)
1468                )
1469
1470
1471            Will produce a query like::
1472
1473                SELECT * FROM my_table WHERE
1474                EXISTS (SELECT 1 FROM related WHERE
1475                related.id==my_table.related_id AND related.x=2)
1476
1477            Because :meth:`~.RelationshipProperty.Comparator.has` uses
1478            a correlated subquery, its performance is not nearly as
1479            good when compared against large target tables as that of
1480            using a join.
1481
1482            :meth:`~.RelationshipProperty.Comparator.has` is only
1483            valid for scalar references, i.e. a :func:`_orm.relationship`
1484            that has ``uselist=False``.  For collection references,
1485            use :meth:`~.RelationshipProperty.Comparator.any`.
1486
1487            """
1488            if self.property.uselist:
1489                raise sa_exc.InvalidRequestError(
1490                    "'has()' not implemented for collections.  " "Use any()."
1491                )
1492            return self._criterion_exists(criterion, **kwargs)
1493
1494        def contains(self, other, **kwargs):
1495            """Return a simple expression that tests a collection for
1496            containment of a particular item.
1497
1498            :meth:`~.RelationshipProperty.Comparator.contains` is
1499            only valid for a collection, i.e. a
1500            :func:`_orm.relationship` that implements
1501            one-to-many or many-to-many with ``uselist=True``.
1502
1503            When used in a simple one-to-many context, an
1504            expression like::
1505
1506                MyClass.contains(other)
1507
1508            Produces a clause like::
1509
1510                mytable.id == <some id>
1511
1512            Where ``<some id>`` is the value of the foreign key
1513            attribute on ``other`` which refers to the primary
1514            key of its parent object. From this it follows that
1515            :meth:`~.RelationshipProperty.Comparator.contains` is
1516            very useful when used with simple one-to-many
1517            operations.
1518
1519            For many-to-many operations, the behavior of
1520            :meth:`~.RelationshipProperty.Comparator.contains`
1521            has more caveats. The association table will be
1522            rendered in the statement, producing an "implicit"
1523            join, that is, includes multiple tables in the FROM
1524            clause which are equated in the WHERE clause::
1525
1526                query(MyClass).filter(MyClass.contains(other))
1527
1528            Produces a query like::
1529
1530                SELECT * FROM my_table, my_association_table AS
1531                my_association_table_1 WHERE
1532                my_table.id = my_association_table_1.parent_id
1533                AND my_association_table_1.child_id = <some id>
1534
1535            Where ``<some id>`` would be the primary key of
1536            ``other``. From the above, it is clear that
1537            :meth:`~.RelationshipProperty.Comparator.contains`
1538            will **not** work with many-to-many collections when
1539            used in queries that move beyond simple AND
1540            conjunctions, such as multiple
1541            :meth:`~.RelationshipProperty.Comparator.contains`
1542            expressions joined by OR. In such cases subqueries or
1543            explicit "outer joins" will need to be used instead.
1544            See :meth:`~.RelationshipProperty.Comparator.any` for
1545            a less-performant alternative using EXISTS, or refer
1546            to :meth:`_query.Query.outerjoin`
1547            as well as :ref:`ormtutorial_joins`
1548            for more details on constructing outer joins.
1549
1550            kwargs may be ignored by this operator but are required for API
1551            conformance.
1552            """
1553            if not self.property.uselist:
1554                raise sa_exc.InvalidRequestError(
1555                    "'contains' not implemented for scalar "
1556                    "attributes.  Use =="
1557                )
1558            clause = self.property._optimized_compare(
1559                other, adapt_source=self.adapter
1560            )
1561
1562            if self.property.secondaryjoin is not None:
1563                clause.negation_clause = self.__negated_contains_or_equals(
1564                    other
1565                )
1566
1567            return clause
1568
1569        def __negated_contains_or_equals(self, other):
1570            if self.property.direction == MANYTOONE:
1571                state = attributes.instance_state(other)
1572
1573                def state_bindparam(local_col, state, remote_col):
1574                    dict_ = state.dict
1575                    return sql.bindparam(
1576                        local_col.key,
1577                        type_=local_col.type,
1578                        unique=True,
1579                        callable_=self.property._get_attr_w_warn_on_none(
1580                            self.property.mapper, state, dict_, remote_col
1581                        ),
1582                    )
1583
1584                def adapt(col):
1585                    if self.adapter:
1586                        return self.adapter(col)
1587                    else:
1588                        return col
1589
1590                if self.property._use_get:
1591                    return sql.and_(
1592                        *[
1593                            sql.or_(
1594                                adapt(x)
1595                                != state_bindparam(adapt(x), state, y),
1596                                adapt(x) == None,
1597                            )
1598                            for (x, y) in self.property.local_remote_pairs
1599                        ]
1600                    )
1601
1602            criterion = sql.and_(
1603                *[
1604                    x == y
1605                    for (x, y) in zip(
1606                        self.property.mapper.primary_key,
1607                        self.property.mapper.primary_key_from_instance(other),
1608                    )
1609                ]
1610            )
1611
1612            return ~self._criterion_exists(criterion)
1613
1614        def __ne__(self, other):
1615            """Implement the ``!=`` operator.
1616
1617            In a many-to-one context, such as::
1618
1619              MyClass.some_prop != <some object>
1620
1621            This will typically produce a clause such as::
1622
1623              mytable.related_id != <some id>
1624
1625            Where ``<some id>`` is the primary key of the
1626            given object.
1627
1628            The ``!=`` operator provides partial functionality for non-
1629            many-to-one comparisons:
1630
1631            * Comparisons against collections are not supported.
1632              Use
1633              :meth:`~.RelationshipProperty.Comparator.contains`
1634              in conjunction with :func:`_expression.not_`.
1635            * Compared to a scalar one-to-many, will produce a
1636              clause that compares the target columns in the parent to
1637              the given target.
1638            * Compared to a scalar many-to-many, an alias
1639              of the association table will be rendered as
1640              well, forming a natural join that is part of the
1641              main body of the query. This will not work for
1642              queries that go beyond simple AND conjunctions of
1643              comparisons, such as those which use OR. Use
1644              explicit joins, outerjoins, or
1645              :meth:`~.RelationshipProperty.Comparator.has` in
1646              conjunction with :func:`_expression.not_` for
1647              more comprehensive non-many-to-one scalar
1648              membership tests.
1649            * Comparisons against ``None`` given in a one-to-many
1650              or many-to-many context produce an EXISTS clause.
1651
1652            """
1653            if isinstance(other, (util.NoneType, expression.Null)):
1654                if self.property.direction == MANYTOONE:
1655                    return _orm_annotate(
1656                        ~self.property._optimized_compare(
1657                            None, adapt_source=self.adapter
1658                        )
1659                    )
1660
1661                else:
1662                    return self._criterion_exists()
1663            elif self.property.uselist:
1664                raise sa_exc.InvalidRequestError(
1665                    "Can't compare a collection"
1666                    " to an object or collection; use "
1667                    "contains() to test for membership."
1668                )
1669            else:
1670                return _orm_annotate(self.__negated_contains_or_equals(other))
1671
1672        @util.memoized_property
1673        def property(self):
1674            self.prop.parent._check_configure()
1675            return self.prop
1676
1677    def _with_parent(self, instance, alias_secondary=True, from_entity=None):
1678        assert instance is not None
1679        adapt_source = None
1680        if from_entity is not None:
1681            insp = inspect(from_entity)
1682            if insp.is_aliased_class:
1683                adapt_source = insp._adapter.adapt_clause
1684        return self._optimized_compare(
1685            instance,
1686            value_is_parent=True,
1687            adapt_source=adapt_source,
1688            alias_secondary=alias_secondary,
1689        )
1690
1691    def _optimized_compare(
1692        self,
1693        state,
1694        value_is_parent=False,
1695        adapt_source=None,
1696        alias_secondary=True,
1697    ):
1698        if state is not None:
1699            try:
1700                state = inspect(state)
1701            except sa_exc.NoInspectionAvailable:
1702                state = None
1703
1704            if state is None or not getattr(state, "is_instance", False):
1705                raise sa_exc.ArgumentError(
1706                    "Mapped instance expected for relationship "
1707                    "comparison to object.   Classes, queries and other "
1708                    "SQL elements are not accepted in this context; for "
1709                    "comparison with a subquery, "
1710                    "use %s.has(**criteria)." % self
1711                )
1712        reverse_direction = not value_is_parent
1713
1714        if state is None:
1715            return self._lazy_none_clause(
1716                reverse_direction, adapt_source=adapt_source
1717            )
1718
1719        if not reverse_direction:
1720            criterion, bind_to_col = (
1721                self._lazy_strategy._lazywhere,
1722                self._lazy_strategy._bind_to_col,
1723            )
1724        else:
1725            criterion, bind_to_col = (
1726                self._lazy_strategy._rev_lazywhere,
1727                self._lazy_strategy._rev_bind_to_col,
1728            )
1729
1730        if reverse_direction:
1731            mapper = self.mapper
1732        else:
1733            mapper = self.parent
1734
1735        dict_ = attributes.instance_dict(state.obj())
1736
1737        def visit_bindparam(bindparam):
1738            if bindparam._identifying_key in bind_to_col:
1739                bindparam.callable = self._get_attr_w_warn_on_none(
1740                    mapper,
1741                    state,
1742                    dict_,
1743                    bind_to_col[bindparam._identifying_key],
1744                )
1745
1746        if self.secondary is not None and alias_secondary:
1747            criterion = ClauseAdapter(
1748                self.secondary._anonymous_fromclause()
1749            ).traverse(criterion)
1750
1751        criterion = visitors.cloned_traverse(
1752            criterion, {}, {"bindparam": visit_bindparam}
1753        )
1754
1755        if adapt_source:
1756            criterion = adapt_source(criterion)
1757        return criterion
1758
1759    def _get_attr_w_warn_on_none(self, mapper, state, dict_, column):
1760        """Create the callable that is used in a many-to-one expression.
1761
1762        E.g.::
1763
1764            u1 = s.query(User).get(5)
1765
1766            expr = Address.user == u1
1767
1768        Above, the SQL should be "address.user_id = 5". The callable
1769        returned by this method produces the value "5" based on the identity
1770        of ``u1``.
1771
1772        """
1773
1774        # in this callable, we're trying to thread the needle through
1775        # a wide variety of scenarios, including:
1776        #
1777        # * the object hasn't been flushed yet and there's no value for
1778        #   the attribute as of yet
1779        #
1780        # * the object hasn't been flushed yet but it has a user-defined
1781        #   value
1782        #
1783        # * the object has a value but it's expired and not locally present
1784        #
1785        # * the object has a value but it's expired and not locally present,
1786        #   and the object is also detached
1787        #
1788        # * The object hadn't been flushed yet, there was no value, but
1789        #   later, the object has been expired and detached, and *now*
1790        #   they're trying to evaluate it
1791        #
1792        # * the object had a value, but it was changed to a new value, and
1793        #   then expired
1794        #
1795        # * the object had a value, but it was changed to a new value, and
1796        #   then expired, then the object was detached
1797        #
1798        # * the object has a user-set value, but it's None and we don't do
1799        #   the comparison correctly for that so warn
1800        #
1801
1802        prop = mapper.get_property_by_column(column)
1803
1804        # by invoking this method, InstanceState will track the last known
1805        # value for this key each time the attribute is to be expired.
1806        # this feature was added explicitly for use in this method.
1807        state._track_last_known_value(prop.key)
1808
1809        def _go():
1810            last_known = to_return = state._last_known_values[prop.key]
1811            existing_is_available = last_known is not attributes.NO_VALUE
1812
1813            # we support that the value may have changed.  so here we
1814            # try to get the most recent value including re-fetching.
1815            # only if we can't get a value now due to detachment do we return
1816            # the last known value
1817            current_value = mapper._get_state_attr_by_column(
1818                state,
1819                dict_,
1820                column,
1821                passive=attributes.PASSIVE_OFF
1822                if state.persistent
1823                else attributes.PASSIVE_NO_FETCH ^ attributes.INIT_OK,
1824            )
1825
1826            if current_value is attributes.NEVER_SET:
1827                if not existing_is_available:
1828                    raise sa_exc.InvalidRequestError(
1829                        "Can't resolve value for column %s on object "
1830                        "%s; no value has been set for this column"
1831                        % (column, state_str(state))
1832                    )
1833            elif current_value is attributes.PASSIVE_NO_RESULT:
1834                if not existing_is_available:
1835                    raise sa_exc.InvalidRequestError(
1836                        "Can't resolve value for column %s on object "
1837                        "%s; the object is detached and the value was "
1838                        "expired" % (column, state_str(state))
1839                    )
1840            else:
1841                to_return = current_value
1842            if to_return is None:
1843                util.warn(
1844                    "Got None for value of column %s; this is unsupported "
1845                    "for a relationship comparison and will not "
1846                    "currently produce an IS comparison "
1847                    "(but may in a future release)" % column
1848                )
1849            return to_return
1850
1851        return _go
1852
1853    def _lazy_none_clause(self, reverse_direction=False, adapt_source=None):
1854        if not reverse_direction:
1855            criterion, bind_to_col = (
1856                self._lazy_strategy._lazywhere,
1857                self._lazy_strategy._bind_to_col,
1858            )
1859        else:
1860            criterion, bind_to_col = (
1861                self._lazy_strategy._rev_lazywhere,
1862                self._lazy_strategy._rev_bind_to_col,
1863            )
1864
1865        criterion = adapt_criterion_to_null(criterion, bind_to_col)
1866
1867        if adapt_source:
1868            criterion = adapt_source(criterion)
1869        return criterion
1870
1871    def __str__(self):
1872        return str(self.parent.class_.__name__) + "." + self.key
1873
1874    def merge(
1875        self,
1876        session,
1877        source_state,
1878        source_dict,
1879        dest_state,
1880        dest_dict,
1881        load,
1882        _recursive,
1883        _resolve_conflict_map,
1884    ):
1885
1886        if load:
1887            for r in self._reverse_property:
1888                if (source_state, r) in _recursive:
1889                    return
1890
1891        if "merge" not in self._cascade:
1892            return
1893
1894        if self.key not in source_dict:
1895            return
1896
1897        if self.uselist:
1898            impl = source_state.get_impl(self.key)
1899            instances_iterable = impl.get_collection(source_state, source_dict)
1900
1901            # if this is a CollectionAttributeImpl, then empty should
1902            # be False, otherwise "self.key in source_dict" should not be
1903            # True
1904            assert not instances_iterable.empty if impl.collection else True
1905
1906            if load:
1907                # for a full merge, pre-load the destination collection,
1908                # so that individual _merge of each item pulls from identity
1909                # map for those already present.
1910                # also assumes CollectionAttributeImpl behavior of loading
1911                # "old" list in any case
1912                dest_state.get_impl(self.key).get(dest_state, dest_dict)
1913
1914            dest_list = []
1915            for current in instances_iterable:
1916                current_state = attributes.instance_state(current)
1917                current_dict = attributes.instance_dict(current)
1918                _recursive[(current_state, self)] = True
1919                obj = session._merge(
1920                    current_state,
1921                    current_dict,
1922                    load=load,
1923                    _recursive=_recursive,
1924                    _resolve_conflict_map=_resolve_conflict_map,
1925                )
1926                if obj is not None:
1927                    dest_list.append(obj)
1928
1929            if not load:
1930                coll = attributes.init_state_collection(
1931                    dest_state, dest_dict, self.key
1932                )
1933                for c in dest_list:
1934                    coll.append_without_event(c)
1935            else:
1936                dest_state.get_impl(self.key).set(
1937                    dest_state, dest_dict, dest_list, _adapt=False
1938                )
1939        else:
1940            current = source_dict[self.key]
1941            if current is not None:
1942                current_state = attributes.instance_state(current)
1943                current_dict = attributes.instance_dict(current)
1944                _recursive[(current_state, self)] = True
1945                obj = session._merge(
1946                    current_state,
1947                    current_dict,
1948                    load=load,
1949                    _recursive=_recursive,
1950                    _resolve_conflict_map=_resolve_conflict_map,
1951                )
1952            else:
1953                obj = None
1954
1955            if not load:
1956                dest_dict[self.key] = obj
1957            else:
1958                dest_state.get_impl(self.key).set(
1959                    dest_state, dest_dict, obj, None
1960                )
1961
1962    def _value_as_iterable(
1963        self, state, dict_, key, passive=attributes.PASSIVE_OFF
1964    ):
1965        """Return a list of tuples (state, obj) for the given
1966        key.
1967
1968        returns an empty list if the value is None/empty/PASSIVE_NO_RESULT
1969        """
1970
1971        impl = state.manager[key].impl
1972        x = impl.get(state, dict_, passive=passive)
1973        if x is attributes.PASSIVE_NO_RESULT or x is None:
1974            return []
1975        elif hasattr(impl, "get_collection"):
1976            return [
1977                (attributes.instance_state(o), o)
1978                for o in impl.get_collection(state, dict_, x, passive=passive)
1979            ]
1980        else:
1981            return [(attributes.instance_state(x), x)]
1982
1983    def cascade_iterator(
1984        self, type_, state, dict_, visited_states, halt_on=None
1985    ):
1986        # assert type_ in self._cascade
1987
1988        # only actively lazy load on the 'delete' cascade
1989        if type_ != "delete" or self.passive_deletes:
1990            passive = attributes.PASSIVE_NO_INITIALIZE
1991        else:
1992            passive = attributes.PASSIVE_OFF
1993
1994        if type_ == "save-update":
1995            tuples = state.manager[self.key].impl.get_all_pending(state, dict_)
1996
1997        else:
1998            tuples = self._value_as_iterable(
1999                state, dict_, self.key, passive=passive
2000            )
2001
2002        skip_pending = (
2003            type_ == "refresh-expire" and "delete-orphan" not in self._cascade
2004        )
2005
2006        for instance_state, c in tuples:
2007            if instance_state in visited_states:
2008                continue
2009
2010            if c is None:
2011                # would like to emit a warning here, but
2012                # would not be consistent with collection.append(None)
2013                # current behavior of silently skipping.
2014                # see [ticket:2229]
2015                continue
2016
2017            instance_dict = attributes.instance_dict(c)
2018
2019            if halt_on and halt_on(instance_state):
2020                continue
2021
2022            if skip_pending and not instance_state.key:
2023                continue
2024
2025            instance_mapper = instance_state.manager.mapper
2026
2027            if not instance_mapper.isa(self.mapper.class_manager.mapper):
2028                raise AssertionError(
2029                    "Attribute '%s' on class '%s' "
2030                    "doesn't handle objects "
2031                    "of type '%s'"
2032                    % (self.key, self.parent.class_, c.__class__)
2033                )
2034
2035            visited_states.add(instance_state)
2036
2037            yield c, instance_mapper, instance_state, instance_dict
2038
2039    @property
2040    def _effective_sync_backref(self):
2041        if self.viewonly:
2042            return False
2043        else:
2044            return self.sync_backref is not False
2045
2046    @staticmethod
2047    def _check_sync_backref(rel_a, rel_b):
2048        if rel_a.viewonly and rel_b.sync_backref:
2049            raise sa_exc.InvalidRequestError(
2050                "Relationship %s cannot specify sync_backref=True since %s "
2051                "includes viewonly=True." % (rel_b, rel_a)
2052            )
2053        if (
2054            rel_a.viewonly
2055            and not rel_b.viewonly
2056            and rel_b.sync_backref is not False
2057        ):
2058            rel_b.sync_backref = False
2059
2060    def _add_reverse_property(self, key):
2061        other = self.mapper.get_property(key, _configure_mappers=False)
2062        if not isinstance(other, RelationshipProperty):
2063            raise sa_exc.InvalidRequestError(
2064                "back_populates on relationship '%s' refers to attribute '%s' "
2065                "that is not a relationship.  The back_populates parameter "
2066                "should refer to the name of a relationship on the target "
2067                "class." % (self, other)
2068            )
2069        # viewonly and sync_backref cases
2070        # 1. self.viewonly==True and other.sync_backref==True -> error
2071        # 2. self.viewonly==True and other.viewonly==False and
2072        #    other.sync_backref==None -> warn sync_backref=False, set to False
2073        self._check_sync_backref(self, other)
2074        # 3. other.viewonly==True and self.sync_backref==True -> error
2075        # 4. other.viewonly==True and self.viewonly==False and
2076        #    self.sync_backref==None -> warn sync_backref=False, set to False
2077        self._check_sync_backref(other, self)
2078
2079        self._reverse_property.add(other)
2080        other._reverse_property.add(self)
2081
2082        if not other.mapper.common_parent(self.parent):
2083            raise sa_exc.ArgumentError(
2084                "reverse_property %r on "
2085                "relationship %s references relationship %s, which "
2086                "does not reference mapper %s"
2087                % (key, self, other, self.parent)
2088            )
2089
2090        if (
2091            self.direction in (ONETOMANY, MANYTOONE)
2092            and self.direction == other.direction
2093        ):
2094            raise sa_exc.ArgumentError(
2095                "%s and back-reference %s are "
2096                "both of the same direction %r.  Did you mean to "
2097                "set remote_side on the many-to-one side ?"
2098                % (other, self, self.direction)
2099            )
2100
2101    @util.memoized_property
2102    @util.preload_module("sqlalchemy.orm.mapper")
2103    def entity(self):
2104        """Return the target mapped entity, which is an inspect() of the
2105        class or aliased class that is referred towards.
2106
2107        """
2108
2109        mapperlib = util.preloaded.orm_mapper
2110
2111        if isinstance(self.argument, util.string_types):
2112            argument = self._clsregistry_resolve_name(self.argument)()
2113
2114        elif callable(self.argument) and not isinstance(
2115            self.argument, (type, mapperlib.Mapper)
2116        ):
2117            argument = self.argument()
2118        else:
2119            argument = self.argument
2120
2121        if isinstance(argument, type):
2122            return mapperlib.class_mapper(argument, configure=False)
2123
2124        try:
2125            entity = inspect(argument)
2126        except sa_exc.NoInspectionAvailable:
2127            pass
2128        else:
2129            if hasattr(entity, "mapper"):
2130                return entity
2131
2132        raise sa_exc.ArgumentError(
2133            "relationship '%s' expects "
2134            "a class or a mapper argument (received: %s)"
2135            % (self.key, type(argument))
2136        )
2137
2138    @util.memoized_property
2139    def mapper(self):
2140        """Return the targeted :class:`_orm.Mapper` for this
2141        :class:`.RelationshipProperty`.
2142
2143        This is a lazy-initializing static attribute.
2144
2145        """
2146        return self.entity.mapper
2147
2148    def do_init(self):
2149        self._check_conflicts()
2150        self._process_dependent_arguments()
2151        self._setup_registry_dependencies()
2152        self._setup_join_conditions()
2153        self._check_cascade_settings(self._cascade)
2154        self._post_init()
2155        self._generate_backref()
2156        self._join_condition._warn_for_conflicting_sync_targets()
2157        super(RelationshipProperty, self).do_init()
2158        self._lazy_strategy = self._get_strategy((("lazy", "select"),))
2159
2160    def _setup_registry_dependencies(self):
2161        self.parent.mapper.registry._set_depends_on(
2162            self.entity.mapper.registry
2163        )
2164
2165    def _process_dependent_arguments(self):
2166        """Convert incoming configuration arguments to their
2167        proper form.
2168
2169        Callables are resolved, ORM annotations removed.
2170
2171        """
2172
2173        # accept callables for other attributes which may require
2174        # deferred initialization.  This technique is used
2175        # by declarative "string configs" and some recipes.
2176        for attr in (
2177            "order_by",
2178            "primaryjoin",
2179            "secondaryjoin",
2180            "secondary",
2181            "_user_defined_foreign_keys",
2182            "remote_side",
2183        ):
2184            attr_value = getattr(self, attr)
2185
2186            if isinstance(attr_value, util.string_types):
2187                setattr(
2188                    self,
2189                    attr,
2190                    self._clsregistry_resolve_arg(
2191                        attr_value, favor_tables=attr == "secondary"
2192                    )(),
2193                )
2194            elif callable(attr_value) and not _is_mapped_class(attr_value):
2195                setattr(self, attr, attr_value())
2196
2197        # remove "annotations" which are present if mapped class
2198        # descriptors are used to create the join expression.
2199        for attr in "primaryjoin", "secondaryjoin":
2200            val = getattr(self, attr)
2201            if val is not None:
2202                setattr(
2203                    self,
2204                    attr,
2205                    _orm_deannotate(
2206                        coercions.expect(
2207                            roles.ColumnArgumentRole, val, argname=attr
2208                        )
2209                    ),
2210                )
2211
2212        if self.secondary is not None and _is_mapped_class(self.secondary):
2213            raise sa_exc.ArgumentError(
2214                "secondary argument %s passed to to relationship() %s must "
2215                "be a Table object or other FROM clause; can't send a mapped "
2216                "class directly as rows in 'secondary' are persisted "
2217                "independently of a class that is mapped "
2218                "to that same table." % (self.secondary, self)
2219            )
2220
2221        # ensure expressions in self.order_by, foreign_keys,
2222        # remote_side are all columns, not strings.
2223        if self.order_by is not False and self.order_by is not None:
2224            self.order_by = tuple(
2225                coercions.expect(
2226                    roles.ColumnArgumentRole, x, argname="order_by"
2227                )
2228                for x in util.to_list(self.order_by)
2229            )
2230
2231        self._user_defined_foreign_keys = util.column_set(
2232            coercions.expect(
2233                roles.ColumnArgumentRole, x, argname="foreign_keys"
2234            )
2235            for x in util.to_column_set(self._user_defined_foreign_keys)
2236        )
2237
2238        self.remote_side = util.column_set(
2239            coercions.expect(
2240                roles.ColumnArgumentRole, x, argname="remote_side"
2241            )
2242            for x in util.to_column_set(self.remote_side)
2243        )
2244
2245        self.target = self.entity.persist_selectable
2246
2247    def _setup_join_conditions(self):
2248        self._join_condition = jc = JoinCondition(
2249            parent_persist_selectable=self.parent.persist_selectable,
2250            child_persist_selectable=self.entity.persist_selectable,
2251            parent_local_selectable=self.parent.local_table,
2252            child_local_selectable=self.entity.local_table,
2253            primaryjoin=self.primaryjoin,
2254            secondary=self.secondary,
2255            secondaryjoin=self.secondaryjoin,
2256            parent_equivalents=self.parent._equivalent_columns,
2257            child_equivalents=self.mapper._equivalent_columns,
2258            consider_as_foreign_keys=self._user_defined_foreign_keys,
2259            local_remote_pairs=self.local_remote_pairs,
2260            remote_side=self.remote_side,
2261            self_referential=self._is_self_referential,
2262            prop=self,
2263            support_sync=not self.viewonly,
2264            can_be_synced_fn=self._columns_are_mapped,
2265        )
2266        self.primaryjoin = jc.primaryjoin
2267        self.secondaryjoin = jc.secondaryjoin
2268        self.direction = jc.direction
2269        self.local_remote_pairs = jc.local_remote_pairs
2270        self.remote_side = jc.remote_columns
2271        self.local_columns = jc.local_columns
2272        self.synchronize_pairs = jc.synchronize_pairs
2273        self._calculated_foreign_keys = jc.foreign_key_columns
2274        self.secondary_synchronize_pairs = jc.secondary_synchronize_pairs
2275
2276    @property
2277    def _clsregistry_resolve_arg(self):
2278        return self._clsregistry_resolvers[1]
2279
2280    @property
2281    def _clsregistry_resolve_name(self):
2282        return self._clsregistry_resolvers[0]
2283
2284    @util.memoized_property
2285    @util.preload_module("sqlalchemy.orm.clsregistry")
2286    def _clsregistry_resolvers(self):
2287        _resolver = util.preloaded.orm_clsregistry._resolver
2288
2289        return _resolver(self.parent.class_, self)
2290
2291    @util.preload_module("sqlalchemy.orm.mapper")
2292    def _check_conflicts(self):
2293        """Test that this relationship is legal, warn about
2294        inheritance conflicts."""
2295        mapperlib = util.preloaded.orm_mapper
2296        if self.parent.non_primary and not mapperlib.class_mapper(
2297            self.parent.class_, configure=False
2298        ).has_property(self.key):
2299            raise sa_exc.ArgumentError(
2300                "Attempting to assign a new "
2301                "relationship '%s' to a non-primary mapper on "
2302                "class '%s'.  New relationships can only be added "
2303                "to the primary mapper, i.e. the very first mapper "
2304                "created for class '%s' "
2305                % (
2306                    self.key,
2307                    self.parent.class_.__name__,
2308                    self.parent.class_.__name__,
2309                )
2310            )
2311
2312    @property
2313    def cascade(self):
2314        """Return the current cascade setting for this
2315        :class:`.RelationshipProperty`.
2316        """
2317        return self._cascade
2318
2319    @cascade.setter
2320    def cascade(self, cascade):
2321        self._set_cascade(cascade)
2322
2323    def _set_cascade(self, cascade):
2324        cascade = CascadeOptions(cascade)
2325
2326        if self.viewonly:
2327            non_viewonly = set(cascade).difference(
2328                CascadeOptions._viewonly_cascades
2329            )
2330            if non_viewonly:
2331                raise sa_exc.ArgumentError(
2332                    'Cascade settings "%s" apply to persistence operations '
2333                    "and should not be combined with a viewonly=True "
2334                    "relationship." % (", ".join(sorted(non_viewonly)))
2335                )
2336
2337        if "mapper" in self.__dict__:
2338            self._check_cascade_settings(cascade)
2339        self._cascade = cascade
2340
2341        if self._dependency_processor:
2342            self._dependency_processor.cascade = cascade
2343
2344    def _check_cascade_settings(self, cascade):
2345        if (
2346            cascade.delete_orphan
2347            and not self.single_parent
2348            and (self.direction is MANYTOMANY or self.direction is MANYTOONE)
2349        ):
2350            raise sa_exc.ArgumentError(
2351                "For %(direction)s relationship %(rel)s, delete-orphan "
2352                "cascade is normally "
2353                'configured only on the "one" side of a one-to-many '
2354                "relationship, "
2355                'and not on the "many" side of a many-to-one or many-to-many '
2356                "relationship.  "
2357                "To force this relationship to allow a particular "
2358                '"%(relatedcls)s" object to be referred towards by only '
2359                'a single "%(clsname)s" object at a time via the '
2360                "%(rel)s relationship, which "
2361                "would allow "
2362                "delete-orphan cascade to take place in this direction, set "
2363                "the single_parent=True flag."
2364                % {
2365                    "rel": self,
2366                    "direction": "many-to-one"
2367                    if self.direction is MANYTOONE
2368                    else "many-to-many",
2369                    "clsname": self.parent.class_.__name__,
2370                    "relatedcls": self.mapper.class_.__name__,
2371                },
2372                code="bbf0",
2373            )
2374
2375        if self.passive_deletes == "all" and (
2376            "delete" in cascade or "delete-orphan" in cascade
2377        ):
2378            raise sa_exc.ArgumentError(
2379                "On %s, can't set passive_deletes='all' in conjunction "
2380                "with 'delete' or 'delete-orphan' cascade" % self
2381            )
2382
2383        if cascade.delete_orphan:
2384            self.mapper.primary_mapper()._delete_orphans.append(
2385                (self.key, self.parent.class_)
2386            )
2387
2388    def _persists_for(self, mapper):
2389        """Return True if this property will persist values on behalf
2390        of the given mapper.
2391
2392        """
2393
2394        return (
2395            self.key in mapper.relationships
2396            and mapper.relationships[self.key] is self
2397        )
2398
2399    def _columns_are_mapped(self, *cols):
2400        """Return True if all columns in the given collection are
2401        mapped by the tables referenced by this :class:`.Relationship`.
2402
2403        """
2404        for c in cols:
2405            if (
2406                self.secondary is not None
2407                and self.secondary.c.contains_column(c)
2408            ):
2409                continue
2410            if not self.parent.persist_selectable.c.contains_column(
2411                c
2412            ) and not self.target.c.contains_column(c):
2413                return False
2414        return True
2415
2416    def _generate_backref(self):
2417        """Interpret the 'backref' instruction to create a
2418        :func:`_orm.relationship` complementary to this one."""
2419
2420        if self.parent.non_primary:
2421            return
2422        if self.backref is not None and not self.back_populates:
2423            if isinstance(self.backref, util.string_types):
2424                backref_key, kwargs = self.backref, {}
2425            else:
2426                backref_key, kwargs = self.backref
2427            mapper = self.mapper.primary_mapper()
2428
2429            if not mapper.concrete:
2430                check = set(mapper.iterate_to_root()).union(
2431                    mapper.self_and_descendants
2432                )
2433                for m in check:
2434                    if m.has_property(backref_key) and not m.concrete:
2435                        raise sa_exc.ArgumentError(
2436                            "Error creating backref "
2437                            "'%s' on relationship '%s': property of that "
2438                            "name exists on mapper '%s'"
2439                            % (backref_key, self, m)
2440                        )
2441
2442            # determine primaryjoin/secondaryjoin for the
2443            # backref.  Use the one we had, so that
2444            # a custom join doesn't have to be specified in
2445            # both directions.
2446            if self.secondary is not None:
2447                # for many to many, just switch primaryjoin/
2448                # secondaryjoin.   use the annotated
2449                # pj/sj on the _join_condition.
2450                pj = kwargs.pop(
2451                    "primaryjoin",
2452                    self._join_condition.secondaryjoin_minus_local,
2453                )
2454                sj = kwargs.pop(
2455                    "secondaryjoin",
2456                    self._join_condition.primaryjoin_minus_local,
2457                )
2458            else:
2459                pj = kwargs.pop(
2460                    "primaryjoin",
2461                    self._join_condition.primaryjoin_reverse_remote,
2462                )
2463                sj = kwargs.pop("secondaryjoin", None)
2464                if sj:
2465                    raise sa_exc.InvalidRequestError(
2466                        "Can't assign 'secondaryjoin' on a backref "
2467                        "against a non-secondary relationship."
2468                    )
2469
2470            foreign_keys = kwargs.pop(
2471                "foreign_keys", self._user_defined_foreign_keys
2472            )
2473            parent = self.parent.primary_mapper()
2474            kwargs.setdefault("viewonly", self.viewonly)
2475            kwargs.setdefault("post_update", self.post_update)
2476            kwargs.setdefault("passive_updates", self.passive_updates)
2477            kwargs.setdefault("sync_backref", self.sync_backref)
2478            self.back_populates = backref_key
2479            relationship = RelationshipProperty(
2480                parent,
2481                self.secondary,
2482                pj,
2483                sj,
2484                foreign_keys=foreign_keys,
2485                back_populates=self.key,
2486                **kwargs
2487            )
2488            mapper._configure_property(backref_key, relationship)
2489
2490        if self.back_populates:
2491            self._add_reverse_property(self.back_populates)
2492
2493    @util.preload_module("sqlalchemy.orm.dependency")
2494    def _post_init(self):
2495        dependency = util.preloaded.orm_dependency
2496
2497        if self.uselist is None:
2498            self.uselist = self.direction is not MANYTOONE
2499        if not self.viewonly:
2500            self._dependency_processor = (
2501                dependency.DependencyProcessor.from_relationship
2502            )(self)
2503
2504    @util.memoized_property
2505    def _use_get(self):
2506        """memoize the 'use_get' attribute of this RelationshipLoader's
2507        lazyloader."""
2508
2509        strategy = self._lazy_strategy
2510        return strategy.use_get
2511
2512    @util.memoized_property
2513    def _is_self_referential(self):
2514        return self.mapper.common_parent(self.parent)
2515
2516    def _create_joins(
2517        self,
2518        source_polymorphic=False,
2519        source_selectable=None,
2520        dest_selectable=None,
2521        of_type_entity=None,
2522        alias_secondary=False,
2523        extra_criteria=(),
2524    ):
2525
2526        aliased = False
2527
2528        if alias_secondary and self.secondary is not None:
2529            aliased = True
2530
2531        if source_selectable is None:
2532            if source_polymorphic and self.parent.with_polymorphic:
2533                source_selectable = self.parent._with_polymorphic_selectable
2534
2535        if of_type_entity:
2536            dest_mapper = of_type_entity.mapper
2537            if dest_selectable is None:
2538                dest_selectable = of_type_entity.selectable
2539                aliased = True
2540        else:
2541            dest_mapper = self.mapper
2542
2543        if dest_selectable is None:
2544            dest_selectable = self.entity.selectable
2545            if self.mapper.with_polymorphic:
2546                aliased = True
2547
2548            if self._is_self_referential and source_selectable is None:
2549                dest_selectable = dest_selectable._anonymous_fromclause()
2550                aliased = True
2551        elif (
2552            dest_selectable is not self.mapper._with_polymorphic_selectable
2553            or self.mapper.with_polymorphic
2554        ):
2555            aliased = True
2556
2557        single_crit = dest_mapper._single_table_criterion
2558        aliased = aliased or (
2559            source_selectable is not None
2560            and (
2561                source_selectable
2562                is not self.parent._with_polymorphic_selectable
2563                or source_selectable._is_subquery
2564            )
2565        )
2566
2567        (
2568            primaryjoin,
2569            secondaryjoin,
2570            secondary,
2571            target_adapter,
2572            dest_selectable,
2573        ) = self._join_condition.join_targets(
2574            source_selectable,
2575            dest_selectable,
2576            aliased,
2577            single_crit,
2578            extra_criteria,
2579        )
2580        if source_selectable is None:
2581            source_selectable = self.parent.local_table
2582        if dest_selectable is None:
2583            dest_selectable = self.entity.local_table
2584        return (
2585            primaryjoin,
2586            secondaryjoin,
2587            source_selectable,
2588            dest_selectable,
2589            secondary,
2590            target_adapter,
2591        )
2592
2593
2594def _annotate_columns(element, annotations):
2595    def clone(elem):
2596        if isinstance(elem, expression.ColumnClause):
2597            elem = elem._annotate(annotations.copy())
2598        elem._copy_internals(clone=clone)
2599        return elem
2600
2601    if element is not None:
2602        element = clone(element)
2603    clone = None  # remove gc cycles
2604    return element
2605
2606
2607class JoinCondition(object):
2608    def __init__(
2609        self,
2610        parent_persist_selectable,
2611        child_persist_selectable,
2612        parent_local_selectable,
2613        child_local_selectable,
2614        primaryjoin=None,
2615        secondary=None,
2616        secondaryjoin=None,
2617        parent_equivalents=None,
2618        child_equivalents=None,
2619        consider_as_foreign_keys=None,
2620        local_remote_pairs=None,
2621        remote_side=None,
2622        self_referential=False,
2623        prop=None,
2624        support_sync=True,
2625        can_be_synced_fn=lambda *c: True,
2626    ):
2627        self.parent_persist_selectable = parent_persist_selectable
2628        self.parent_local_selectable = parent_local_selectable
2629        self.child_persist_selectable = child_persist_selectable
2630        self.child_local_selectable = child_local_selectable
2631        self.parent_equivalents = parent_equivalents
2632        self.child_equivalents = child_equivalents
2633        self.primaryjoin = primaryjoin
2634        self.secondaryjoin = secondaryjoin
2635        self.secondary = secondary
2636        self.consider_as_foreign_keys = consider_as_foreign_keys
2637        self._local_remote_pairs = local_remote_pairs
2638        self._remote_side = remote_side
2639        self.prop = prop
2640        self.self_referential = self_referential
2641        self.support_sync = support_sync
2642        self.can_be_synced_fn = can_be_synced_fn
2643        self._determine_joins()
2644        self._sanitize_joins()
2645        self._annotate_fks()
2646        self._annotate_remote()
2647        self._annotate_local()
2648        self._annotate_parentmapper()
2649        self._setup_pairs()
2650        self._check_foreign_cols(self.primaryjoin, True)
2651        if self.secondaryjoin is not None:
2652            self._check_foreign_cols(self.secondaryjoin, False)
2653        self._determine_direction()
2654        self._check_remote_side()
2655        self._log_joins()
2656
2657    def _log_joins(self):
2658        if self.prop is None:
2659            return
2660        log = self.prop.logger
2661        log.info("%s setup primary join %s", self.prop, self.primaryjoin)
2662        log.info("%s setup secondary join %s", self.prop, self.secondaryjoin)
2663        log.info(
2664            "%s synchronize pairs [%s]",
2665            self.prop,
2666            ",".join(
2667                "(%s => %s)" % (l, r) for (l, r) in self.synchronize_pairs
2668            ),
2669        )
2670        log.info(
2671            "%s secondary synchronize pairs [%s]",
2672            self.prop,
2673            ",".join(
2674                "(%s => %s)" % (l, r)
2675                for (l, r) in self.secondary_synchronize_pairs or []
2676            ),
2677        )
2678        log.info(
2679            "%s local/remote pairs [%s]",
2680            self.prop,
2681            ",".join(
2682                "(%s / %s)" % (l, r) for (l, r) in self.local_remote_pairs
2683            ),
2684        )
2685        log.info(
2686            "%s remote columns [%s]",
2687            self.prop,
2688            ",".join("%s" % col for col in self.remote_columns),
2689        )
2690        log.info(
2691            "%s local columns [%s]",
2692            self.prop,
2693            ",".join("%s" % col for col in self.local_columns),
2694        )
2695        log.info("%s relationship direction %s", self.prop, self.direction)
2696
2697    def _sanitize_joins(self):
2698        """remove the parententity annotation from our join conditions which
2699        can leak in here based on some declarative patterns and maybe others.
2700
2701        We'd want to remove "parentmapper" also, but apparently there's
2702        an exotic use case in _join_fixture_inh_selfref_w_entity
2703        that relies upon it being present, see :ticket:`3364`.
2704
2705        """
2706
2707        self.primaryjoin = _deep_deannotate(
2708            self.primaryjoin, values=("parententity", "proxy_key")
2709        )
2710        if self.secondaryjoin is not None:
2711            self.secondaryjoin = _deep_deannotate(
2712                self.secondaryjoin, values=("parententity", "proxy_key")
2713            )
2714
2715    def _determine_joins(self):
2716        """Determine the 'primaryjoin' and 'secondaryjoin' attributes,
2717        if not passed to the constructor already.
2718
2719        This is based on analysis of the foreign key relationships
2720        between the parent and target mapped selectables.
2721
2722        """
2723        if self.secondaryjoin is not None and self.secondary is None:
2724            raise sa_exc.ArgumentError(
2725                "Property %s specified with secondary "
2726                "join condition but "
2727                "no secondary argument" % self.prop
2728            )
2729
2730        # find a join between the given mapper's mapped table and
2731        # the given table. will try the mapper's local table first
2732        # for more specificity, then if not found will try the more
2733        # general mapped table, which in the case of inheritance is
2734        # a join.
2735        try:
2736            consider_as_foreign_keys = self.consider_as_foreign_keys or None
2737            if self.secondary is not None:
2738                if self.secondaryjoin is None:
2739                    self.secondaryjoin = join_condition(
2740                        self.child_persist_selectable,
2741                        self.secondary,
2742                        a_subset=self.child_local_selectable,
2743                        consider_as_foreign_keys=consider_as_foreign_keys,
2744                    )
2745                if self.primaryjoin is None:
2746                    self.primaryjoin = join_condition(
2747                        self.parent_persist_selectable,
2748                        self.secondary,
2749                        a_subset=self.parent_local_selectable,
2750                        consider_as_foreign_keys=consider_as_foreign_keys,
2751                    )
2752            else:
2753                if self.primaryjoin is None:
2754                    self.primaryjoin = join_condition(
2755                        self.parent_persist_selectable,
2756                        self.child_persist_selectable,
2757                        a_subset=self.parent_local_selectable,
2758                        consider_as_foreign_keys=consider_as_foreign_keys,
2759                    )
2760        except sa_exc.NoForeignKeysError as nfe:
2761            if self.secondary is not None:
2762                util.raise_(
2763                    sa_exc.NoForeignKeysError(
2764                        "Could not determine join "
2765                        "condition between parent/child tables on "
2766                        "relationship %s - there are no foreign keys "
2767                        "linking these tables via secondary table '%s'.  "
2768                        "Ensure that referencing columns are associated "
2769                        "with a ForeignKey or ForeignKeyConstraint, or "
2770                        "specify 'primaryjoin' and 'secondaryjoin' "
2771                        "expressions." % (self.prop, self.secondary)
2772                    ),
2773                    from_=nfe,
2774                )
2775            else:
2776                util.raise_(
2777                    sa_exc.NoForeignKeysError(
2778                        "Could not determine join "
2779                        "condition between parent/child tables on "
2780                        "relationship %s - there are no foreign keys "
2781                        "linking these tables.  "
2782                        "Ensure that referencing columns are associated "
2783                        "with a ForeignKey or ForeignKeyConstraint, or "
2784                        "specify a 'primaryjoin' expression." % self.prop
2785                    ),
2786                    from_=nfe,
2787                )
2788        except sa_exc.AmbiguousForeignKeysError as afe:
2789            if self.secondary is not None:
2790                util.raise_(
2791                    sa_exc.AmbiguousForeignKeysError(
2792                        "Could not determine join "
2793                        "condition between parent/child tables on "
2794                        "relationship %s - there are multiple foreign key "
2795                        "paths linking the tables via secondary table '%s'.  "
2796                        "Specify the 'foreign_keys' "
2797                        "argument, providing a list of those columns which "
2798                        "should be counted as containing a foreign key "
2799                        "reference from the secondary table to each of the "
2800                        "parent and child tables."
2801                        % (self.prop, self.secondary)
2802                    ),
2803                    from_=afe,
2804                )
2805            else:
2806                util.raise_(
2807                    sa_exc.AmbiguousForeignKeysError(
2808                        "Could not determine join "
2809                        "condition between parent/child tables on "
2810                        "relationship %s - there are multiple foreign key "
2811                        "paths linking the tables.  Specify the "
2812                        "'foreign_keys' argument, providing a list of those "
2813                        "columns which should be counted as containing a "
2814                        "foreign key reference to the parent table."
2815                        % self.prop
2816                    ),
2817                    from_=afe,
2818                )
2819
2820    @property
2821    def primaryjoin_minus_local(self):
2822        return _deep_deannotate(self.primaryjoin, values=("local", "remote"))
2823
2824    @property
2825    def secondaryjoin_minus_local(self):
2826        return _deep_deannotate(self.secondaryjoin, values=("local", "remote"))
2827
2828    @util.memoized_property
2829    def primaryjoin_reverse_remote(self):
2830        """Return the primaryjoin condition suitable for the
2831        "reverse" direction.
2832
2833        If the primaryjoin was delivered here with pre-existing
2834        "remote" annotations, the local/remote annotations
2835        are reversed.  Otherwise, the local/remote annotations
2836        are removed.
2837
2838        """
2839        if self._has_remote_annotations:
2840
2841            def replace(element):
2842                if "remote" in element._annotations:
2843                    v = dict(element._annotations)
2844                    del v["remote"]
2845                    v["local"] = True
2846                    return element._with_annotations(v)
2847                elif "local" in element._annotations:
2848                    v = dict(element._annotations)
2849                    del v["local"]
2850                    v["remote"] = True
2851                    return element._with_annotations(v)
2852
2853            return visitors.replacement_traverse(self.primaryjoin, {}, replace)
2854        else:
2855            if self._has_foreign_annotations:
2856                # TODO: coverage
2857                return _deep_deannotate(
2858                    self.primaryjoin, values=("local", "remote")
2859                )
2860            else:
2861                return _deep_deannotate(self.primaryjoin)
2862
2863    def _has_annotation(self, clause, annotation):
2864        for col in visitors.iterate(clause, {}):
2865            if annotation in col._annotations:
2866                return True
2867        else:
2868            return False
2869
2870    @util.memoized_property
2871    def _has_foreign_annotations(self):
2872        return self._has_annotation(self.primaryjoin, "foreign")
2873
2874    @util.memoized_property
2875    def _has_remote_annotations(self):
2876        return self._has_annotation(self.primaryjoin, "remote")
2877
2878    def _annotate_fks(self):
2879        """Annotate the primaryjoin and secondaryjoin
2880        structures with 'foreign' annotations marking columns
2881        considered as foreign.
2882
2883        """
2884        if self._has_foreign_annotations:
2885            return
2886
2887        if self.consider_as_foreign_keys:
2888            self._annotate_from_fk_list()
2889        else:
2890            self._annotate_present_fks()
2891
2892    def _annotate_from_fk_list(self):
2893        def check_fk(col):
2894            if col in self.consider_as_foreign_keys:
2895                return col._annotate({"foreign": True})
2896
2897        self.primaryjoin = visitors.replacement_traverse(
2898            self.primaryjoin, {}, check_fk
2899        )
2900        if self.secondaryjoin is not None:
2901            self.secondaryjoin = visitors.replacement_traverse(
2902                self.secondaryjoin, {}, check_fk
2903            )
2904
2905    def _annotate_present_fks(self):
2906        if self.secondary is not None:
2907            secondarycols = util.column_set(self.secondary.c)
2908        else:
2909            secondarycols = set()
2910
2911        def is_foreign(a, b):
2912            if isinstance(a, schema.Column) and isinstance(b, schema.Column):
2913                if a.references(b):
2914                    return a
2915                elif b.references(a):
2916                    return b
2917
2918            if secondarycols:
2919                if a in secondarycols and b not in secondarycols:
2920                    return a
2921                elif b in secondarycols and a not in secondarycols:
2922                    return b
2923
2924        def visit_binary(binary):
2925            if not isinstance(
2926                binary.left, sql.ColumnElement
2927            ) or not isinstance(binary.right, sql.ColumnElement):
2928                return
2929
2930            if (
2931                "foreign" not in binary.left._annotations
2932                and "foreign" not in binary.right._annotations
2933            ):
2934                col = is_foreign(binary.left, binary.right)
2935                if col is not None:
2936                    if col.compare(binary.left):
2937                        binary.left = binary.left._annotate({"foreign": True})
2938                    elif col.compare(binary.right):
2939                        binary.right = binary.right._annotate(
2940                            {"foreign": True}
2941                        )
2942
2943        self.primaryjoin = visitors.cloned_traverse(
2944            self.primaryjoin, {}, {"binary": visit_binary}
2945        )
2946        if self.secondaryjoin is not None:
2947            self.secondaryjoin = visitors.cloned_traverse(
2948                self.secondaryjoin, {}, {"binary": visit_binary}
2949            )
2950
2951    def _refers_to_parent_table(self):
2952        """Return True if the join condition contains column
2953        comparisons where both columns are in both tables.
2954
2955        """
2956        pt = self.parent_persist_selectable
2957        mt = self.child_persist_selectable
2958        result = [False]
2959
2960        def visit_binary(binary):
2961            c, f = binary.left, binary.right
2962            if (
2963                isinstance(c, expression.ColumnClause)
2964                and isinstance(f, expression.ColumnClause)
2965                and pt.is_derived_from(c.table)
2966                and pt.is_derived_from(f.table)
2967                and mt.is_derived_from(c.table)
2968                and mt.is_derived_from(f.table)
2969            ):
2970                result[0] = True
2971
2972        visitors.traverse(self.primaryjoin, {}, {"binary": visit_binary})
2973        return result[0]
2974
2975    def _tables_overlap(self):
2976        """Return True if parent/child tables have some overlap."""
2977
2978        return selectables_overlap(
2979            self.parent_persist_selectable, self.child_persist_selectable
2980        )
2981
2982    def _annotate_remote(self):
2983        """Annotate the primaryjoin and secondaryjoin
2984        structures with 'remote' annotations marking columns
2985        considered as part of the 'remote' side.
2986
2987        """
2988        if self._has_remote_annotations:
2989            return
2990
2991        if self.secondary is not None:
2992            self._annotate_remote_secondary()
2993        elif self._local_remote_pairs or self._remote_side:
2994            self._annotate_remote_from_args()
2995        elif self._refers_to_parent_table():
2996            self._annotate_selfref(
2997                lambda col: "foreign" in col._annotations, False
2998            )
2999        elif self._tables_overlap():
3000            self._annotate_remote_with_overlap()
3001        else:
3002            self._annotate_remote_distinct_selectables()
3003
3004    def _annotate_remote_secondary(self):
3005        """annotate 'remote' in primaryjoin, secondaryjoin
3006        when 'secondary' is present.
3007
3008        """
3009
3010        def repl(element):
3011            if self.secondary.c.contains_column(element):
3012                return element._annotate({"remote": True})
3013
3014        self.primaryjoin = visitors.replacement_traverse(
3015            self.primaryjoin, {}, repl
3016        )
3017        self.secondaryjoin = visitors.replacement_traverse(
3018            self.secondaryjoin, {}, repl
3019        )
3020
3021    def _annotate_selfref(self, fn, remote_side_given):
3022        """annotate 'remote' in primaryjoin, secondaryjoin
3023        when the relationship is detected as self-referential.
3024
3025        """
3026
3027        def visit_binary(binary):
3028            equated = binary.left.compare(binary.right)
3029            if isinstance(binary.left, expression.ColumnClause) and isinstance(
3030                binary.right, expression.ColumnClause
3031            ):
3032                # assume one to many - FKs are "remote"
3033                if fn(binary.left):
3034                    binary.left = binary.left._annotate({"remote": True})
3035                if fn(binary.right) and not equated:
3036                    binary.right = binary.right._annotate({"remote": True})
3037            elif not remote_side_given:
3038                self._warn_non_column_elements()
3039
3040        self.primaryjoin = visitors.cloned_traverse(
3041            self.primaryjoin, {}, {"binary": visit_binary}
3042        )
3043
3044    def _annotate_remote_from_args(self):
3045        """annotate 'remote' in primaryjoin, secondaryjoin
3046        when the 'remote_side' or '_local_remote_pairs'
3047        arguments are used.
3048
3049        """
3050        if self._local_remote_pairs:
3051            if self._remote_side:
3052                raise sa_exc.ArgumentError(
3053                    "remote_side argument is redundant "
3054                    "against more detailed _local_remote_side "
3055                    "argument."
3056                )
3057
3058            remote_side = [r for (l, r) in self._local_remote_pairs]
3059        else:
3060            remote_side = self._remote_side
3061
3062        if self._refers_to_parent_table():
3063            self._annotate_selfref(lambda col: col in remote_side, True)
3064        else:
3065
3066            def repl(element):
3067                # use set() to avoid generating ``__eq__()`` expressions
3068                # against each element
3069                if element in set(remote_side):
3070                    return element._annotate({"remote": True})
3071
3072            self.primaryjoin = visitors.replacement_traverse(
3073                self.primaryjoin, {}, repl
3074            )
3075
3076    def _annotate_remote_with_overlap(self):
3077        """annotate 'remote' in primaryjoin, secondaryjoin
3078        when the parent/child tables have some set of
3079        tables in common, though is not a fully self-referential
3080        relationship.
3081
3082        """
3083
3084        def visit_binary(binary):
3085            binary.left, binary.right = proc_left_right(
3086                binary.left, binary.right
3087            )
3088            binary.right, binary.left = proc_left_right(
3089                binary.right, binary.left
3090            )
3091
3092        check_entities = (
3093            self.prop is not None and self.prop.mapper is not self.prop.parent
3094        )
3095
3096        def proc_left_right(left, right):
3097            if isinstance(left, expression.ColumnClause) and isinstance(
3098                right, expression.ColumnClause
3099            ):
3100                if self.child_persist_selectable.c.contains_column(
3101                    right
3102                ) and self.parent_persist_selectable.c.contains_column(left):
3103                    right = right._annotate({"remote": True})
3104            elif (
3105                check_entities
3106                and right._annotations.get("parentmapper") is self.prop.mapper
3107            ):
3108                right = right._annotate({"remote": True})
3109            elif (
3110                check_entities
3111                and left._annotations.get("parentmapper") is self.prop.mapper
3112            ):
3113                left = left._annotate({"remote": True})
3114            else:
3115                self._warn_non_column_elements()
3116
3117            return left, right
3118
3119        self.primaryjoin = visitors.cloned_traverse(
3120            self.primaryjoin, {}, {"binary": visit_binary}
3121        )
3122
3123    def _annotate_remote_distinct_selectables(self):
3124        """annotate 'remote' in primaryjoin, secondaryjoin
3125        when the parent/child tables are entirely
3126        separate.
3127
3128        """
3129
3130        def repl(element):
3131            if self.child_persist_selectable.c.contains_column(element) and (
3132                not self.parent_local_selectable.c.contains_column(element)
3133                or self.child_local_selectable.c.contains_column(element)
3134            ):
3135                return element._annotate({"remote": True})
3136
3137        self.primaryjoin = visitors.replacement_traverse(
3138            self.primaryjoin, {}, repl
3139        )
3140
3141    def _warn_non_column_elements(self):
3142        util.warn(
3143            "Non-simple column elements in primary "
3144            "join condition for property %s - consider using "
3145            "remote() annotations to mark the remote side." % self.prop
3146        )
3147
3148    def _annotate_local(self):
3149        """Annotate the primaryjoin and secondaryjoin
3150        structures with 'local' annotations.
3151
3152        This annotates all column elements found
3153        simultaneously in the parent table
3154        and the join condition that don't have a
3155        'remote' annotation set up from
3156        _annotate_remote() or user-defined.
3157
3158        """
3159        if self._has_annotation(self.primaryjoin, "local"):
3160            return
3161
3162        if self._local_remote_pairs:
3163            local_side = util.column_set(
3164                [l for (l, r) in self._local_remote_pairs]
3165            )
3166        else:
3167            local_side = util.column_set(self.parent_persist_selectable.c)
3168
3169        def locals_(elem):
3170            if "remote" not in elem._annotations and elem in local_side:
3171                return elem._annotate({"local": True})
3172
3173        self.primaryjoin = visitors.replacement_traverse(
3174            self.primaryjoin, {}, locals_
3175        )
3176
3177    def _annotate_parentmapper(self):
3178        if self.prop is None:
3179            return
3180
3181        def parentmappers_(elem):
3182            if "remote" in elem._annotations:
3183                return elem._annotate({"parentmapper": self.prop.mapper})
3184            elif "local" in elem._annotations:
3185                return elem._annotate({"parentmapper": self.prop.parent})
3186
3187        self.primaryjoin = visitors.replacement_traverse(
3188            self.primaryjoin, {}, parentmappers_
3189        )
3190
3191    def _check_remote_side(self):
3192        if not self.local_remote_pairs:
3193            raise sa_exc.ArgumentError(
3194                "Relationship %s could "
3195                "not determine any unambiguous local/remote column "
3196                "pairs based on join condition and remote_side "
3197                "arguments.  "
3198                "Consider using the remote() annotation to "
3199                "accurately mark those elements of the join "
3200                "condition that are on the remote side of "
3201                "the relationship." % (self.prop,)
3202            )
3203
3204    def _check_foreign_cols(self, join_condition, primary):
3205        """Check the foreign key columns collected and emit error
3206        messages."""
3207
3208        can_sync = False
3209
3210        foreign_cols = self._gather_columns_with_annotation(
3211            join_condition, "foreign"
3212        )
3213
3214        has_foreign = bool(foreign_cols)
3215
3216        if primary:
3217            can_sync = bool(self.synchronize_pairs)
3218        else:
3219            can_sync = bool(self.secondary_synchronize_pairs)
3220
3221        if (
3222            self.support_sync
3223            and can_sync
3224            or (not self.support_sync and has_foreign)
3225        ):
3226            return
3227
3228        # from here below is just determining the best error message
3229        # to report.  Check for a join condition using any operator
3230        # (not just ==), perhaps they need to turn on "viewonly=True".
3231        if self.support_sync and has_foreign and not can_sync:
3232            err = (
3233                "Could not locate any simple equality expressions "
3234                "involving locally mapped foreign key columns for "
3235                "%s join condition "
3236                "'%s' on relationship %s."
3237                % (
3238                    primary and "primary" or "secondary",
3239                    join_condition,
3240                    self.prop,
3241                )
3242            )
3243            err += (
3244                "  Ensure that referencing columns are associated "
3245                "with a ForeignKey or ForeignKeyConstraint, or are "
3246                "annotated in the join condition with the foreign() "
3247                "annotation. To allow comparison operators other than "
3248                "'==', the relationship can be marked as viewonly=True."
3249            )
3250
3251            raise sa_exc.ArgumentError(err)
3252        else:
3253            err = (
3254                "Could not locate any relevant foreign key columns "
3255                "for %s join condition '%s' on relationship %s."
3256                % (
3257                    primary and "primary" or "secondary",
3258                    join_condition,
3259                    self.prop,
3260                )
3261            )
3262            err += (
3263                "  Ensure that referencing columns are associated "
3264                "with a ForeignKey or ForeignKeyConstraint, or are "
3265                "annotated in the join condition with the foreign() "
3266                "annotation."
3267            )
3268            raise sa_exc.ArgumentError(err)
3269
3270    def _determine_direction(self):
3271        """Determine if this relationship is one to many, many to one,
3272        many to many.
3273
3274        """
3275        if self.secondaryjoin is not None:
3276            self.direction = MANYTOMANY
3277        else:
3278            parentcols = util.column_set(self.parent_persist_selectable.c)
3279            targetcols = util.column_set(self.child_persist_selectable.c)
3280
3281            # fk collection which suggests ONETOMANY.
3282            onetomany_fk = targetcols.intersection(self.foreign_key_columns)
3283
3284            # fk collection which suggests MANYTOONE.
3285
3286            manytoone_fk = parentcols.intersection(self.foreign_key_columns)
3287
3288            if onetomany_fk and manytoone_fk:
3289                # fks on both sides.  test for overlap of local/remote
3290                # with foreign key.
3291                # we will gather columns directly from their annotations
3292                # without deannotating, so that we can distinguish on a column
3293                # that refers to itself.
3294
3295                # 1. columns that are both remote and FK suggest
3296                # onetomany.
3297                onetomany_local = self._gather_columns_with_annotation(
3298                    self.primaryjoin, "remote", "foreign"
3299                )
3300
3301                # 2. columns that are FK but are not remote (e.g. local)
3302                # suggest manytoone.
3303                manytoone_local = set(
3304                    [
3305                        c
3306                        for c in self._gather_columns_with_annotation(
3307                            self.primaryjoin, "foreign"
3308                        )
3309                        if "remote" not in c._annotations
3310                    ]
3311                )
3312
3313                # 3. if both collections are present, remove columns that
3314                # refer to themselves.  This is for the case of
3315                # and_(Me.id == Me.remote_id, Me.version == Me.version)
3316                if onetomany_local and manytoone_local:
3317                    self_equated = self.remote_columns.intersection(
3318                        self.local_columns
3319                    )
3320                    onetomany_local = onetomany_local.difference(self_equated)
3321                    manytoone_local = manytoone_local.difference(self_equated)
3322
3323                # at this point, if only one or the other collection is
3324                # present, we know the direction, otherwise it's still
3325                # ambiguous.
3326
3327                if onetomany_local and not manytoone_local:
3328                    self.direction = ONETOMANY
3329                elif manytoone_local and not onetomany_local:
3330                    self.direction = MANYTOONE
3331                else:
3332                    raise sa_exc.ArgumentError(
3333                        "Can't determine relationship"
3334                        " direction for relationship '%s' - foreign "
3335                        "key columns within the join condition are present "
3336                        "in both the parent and the child's mapped tables.  "
3337                        "Ensure that only those columns referring "
3338                        "to a parent column are marked as foreign, "
3339                        "either via the foreign() annotation or "
3340                        "via the foreign_keys argument." % self.prop
3341                    )
3342            elif onetomany_fk:
3343                self.direction = ONETOMANY
3344            elif manytoone_fk:
3345                self.direction = MANYTOONE
3346            else:
3347                raise sa_exc.ArgumentError(
3348                    "Can't determine relationship "
3349                    "direction for relationship '%s' - foreign "
3350                    "key columns are present in neither the parent "
3351                    "nor the child's mapped tables" % self.prop
3352                )
3353
3354    def _deannotate_pairs(self, collection):
3355        """provide deannotation for the various lists of
3356        pairs, so that using them in hashes doesn't incur
3357        high-overhead __eq__() comparisons against
3358        original columns mapped.
3359
3360        """
3361        return [(x._deannotate(), y._deannotate()) for x, y in collection]
3362
3363    def _setup_pairs(self):
3364        sync_pairs = []
3365        lrp = util.OrderedSet([])
3366        secondary_sync_pairs = []
3367
3368        def go(joincond, collection):
3369            def visit_binary(binary, left, right):
3370                if (
3371                    "remote" in right._annotations
3372                    and "remote" not in left._annotations
3373                    and self.can_be_synced_fn(left)
3374                ):
3375                    lrp.add((left, right))
3376                elif (
3377                    "remote" in left._annotations
3378                    and "remote" not in right._annotations
3379                    and self.can_be_synced_fn(right)
3380                ):
3381                    lrp.add((right, left))
3382                if binary.operator is operators.eq and self.can_be_synced_fn(
3383                    left, right
3384                ):
3385                    if "foreign" in right._annotations:
3386                        collection.append((left, right))
3387                    elif "foreign" in left._annotations:
3388                        collection.append((right, left))
3389
3390            visit_binary_product(visit_binary, joincond)
3391
3392        for joincond, collection in [
3393            (self.primaryjoin, sync_pairs),
3394            (self.secondaryjoin, secondary_sync_pairs),
3395        ]:
3396            if joincond is None:
3397                continue
3398            go(joincond, collection)
3399
3400        self.local_remote_pairs = self._deannotate_pairs(lrp)
3401        self.synchronize_pairs = self._deannotate_pairs(sync_pairs)
3402        self.secondary_synchronize_pairs = self._deannotate_pairs(
3403            secondary_sync_pairs
3404        )
3405
3406    _track_overlapping_sync_targets = weakref.WeakKeyDictionary()
3407
3408    def _warn_for_conflicting_sync_targets(self):
3409        if not self.support_sync:
3410            return
3411
3412        # we would like to detect if we are synchronizing any column
3413        # pairs in conflict with another relationship that wishes to sync
3414        # an entirely different column to the same target.   This is a
3415        # very rare edge case so we will try to minimize the memory/overhead
3416        # impact of this check
3417        for from_, to_ in [
3418            (from_, to_) for (from_, to_) in self.synchronize_pairs
3419        ] + [
3420            (from_, to_) for (from_, to_) in self.secondary_synchronize_pairs
3421        ]:
3422            # save ourselves a ton of memory and overhead by only
3423            # considering columns that are subject to a overlapping
3424            # FK constraints at the core level.   This condition can arise
3425            # if multiple relationships overlap foreign() directly, but
3426            # we're going to assume it's typically a ForeignKeyConstraint-
3427            # level configuration that benefits from this warning.
3428
3429            if to_ not in self._track_overlapping_sync_targets:
3430                self._track_overlapping_sync_targets[
3431                    to_
3432                ] = weakref.WeakKeyDictionary({self.prop: from_})
3433            else:
3434                other_props = []
3435                prop_to_from = self._track_overlapping_sync_targets[to_]
3436
3437                for pr, fr_ in prop_to_from.items():
3438                    if (
3439                        not pr.mapper._dispose_called
3440                        and pr not in self.prop._reverse_property
3441                        and pr.key not in self.prop._overlaps
3442                        and self.prop.key not in pr._overlaps
3443                        # note: the "__*" symbol is used internally by
3444                        # SQLAlchemy as a general means of suppressing the
3445                        # overlaps warning for some extension cases, however
3446                        # this is not currently
3447                        # a publicly supported symbol and may change at
3448                        # any time.
3449                        and "__*" not in self.prop._overlaps
3450                        and "__*" not in pr._overlaps
3451                        and not self.prop.parent.is_sibling(pr.parent)
3452                        and not self.prop.mapper.is_sibling(pr.mapper)
3453                        and not self.prop.parent.is_sibling(pr.mapper)
3454                        and not self.prop.mapper.is_sibling(pr.parent)
3455                        and (
3456                            self.prop.key != pr.key
3457                            or not self.prop.parent.common_parent(pr.parent)
3458                        )
3459                    ):
3460
3461                        other_props.append((pr, fr_))
3462
3463                if other_props:
3464                    util.warn(
3465                        "relationship '%s' will copy column %s to column %s, "
3466                        "which conflicts with relationship(s): %s. "
3467                        "If this is not the intention, consider if these "
3468                        "relationships should be linked with "
3469                        "back_populates, or if viewonly=True should be "
3470                        "applied to one or more if they are read-only. "
3471                        "For the less common case that foreign key "
3472                        "constraints are partially overlapping, the "
3473                        "orm.foreign() "
3474                        "annotation can be used to isolate the columns that "
3475                        "should be written towards.   To silence this "
3476                        "warning, add the parameter 'overlaps=\"%s\"' to the "
3477                        "'%s' relationship."
3478                        % (
3479                            self.prop,
3480                            from_,
3481                            to_,
3482                            ", ".join(
3483                                sorted(
3484                                    "'%s' (copies %s to %s)" % (pr, fr_, to_)
3485                                    for (pr, fr_) in other_props
3486                                )
3487                            ),
3488                            ",".join(sorted(pr.key for pr, fr in other_props)),
3489                            self.prop,
3490                        ),
3491                        code="qzyx",
3492                    )
3493                self._track_overlapping_sync_targets[to_][self.prop] = from_
3494
3495    @util.memoized_property
3496    def remote_columns(self):
3497        return self._gather_join_annotations("remote")
3498
3499    @util.memoized_property
3500    def local_columns(self):
3501        return self._gather_join_annotations("local")
3502
3503    @util.memoized_property
3504    def foreign_key_columns(self):
3505        return self._gather_join_annotations("foreign")
3506
3507    def _gather_join_annotations(self, annotation):
3508        s = set(
3509            self._gather_columns_with_annotation(self.primaryjoin, annotation)
3510        )
3511        if self.secondaryjoin is not None:
3512            s.update(
3513                self._gather_columns_with_annotation(
3514                    self.secondaryjoin, annotation
3515                )
3516            )
3517        return {x._deannotate() for x in s}
3518
3519    def _gather_columns_with_annotation(self, clause, *annotation):
3520        annotation = set(annotation)
3521        return set(
3522            [
3523                col
3524                for col in visitors.iterate(clause, {})
3525                if annotation.issubset(col._annotations)
3526            ]
3527        )
3528
3529    def join_targets(
3530        self,
3531        source_selectable,
3532        dest_selectable,
3533        aliased,
3534        single_crit=None,
3535        extra_criteria=(),
3536    ):
3537        """Given a source and destination selectable, create a
3538        join between them.
3539
3540        This takes into account aliasing the join clause
3541        to reference the appropriate corresponding columns
3542        in the target objects, as well as the extra child
3543        criterion, equivalent column sets, etc.
3544
3545        """
3546        # place a barrier on the destination such that
3547        # replacement traversals won't ever dig into it.
3548        # its internal structure remains fixed
3549        # regardless of context.
3550        dest_selectable = _shallow_annotate(
3551            dest_selectable, {"no_replacement_traverse": True}
3552        )
3553
3554        primaryjoin, secondaryjoin, secondary = (
3555            self.primaryjoin,
3556            self.secondaryjoin,
3557            self.secondary,
3558        )
3559
3560        # adjust the join condition for single table inheritance,
3561        # in the case that the join is to a subclass
3562        # this is analogous to the
3563        # "_adjust_for_single_table_inheritance()" method in Query.
3564
3565        if single_crit is not None:
3566            if secondaryjoin is not None:
3567                secondaryjoin = secondaryjoin & single_crit
3568            else:
3569                primaryjoin = primaryjoin & single_crit
3570
3571        if extra_criteria:
3572            if secondaryjoin is not None:
3573                secondaryjoin = secondaryjoin & sql.and_(*extra_criteria)
3574            else:
3575                primaryjoin = primaryjoin & sql.and_(*extra_criteria)
3576
3577        if aliased:
3578            if secondary is not None:
3579                secondary = secondary._anonymous_fromclause(flat=True)
3580                primary_aliasizer = ClauseAdapter(
3581                    secondary, exclude_fn=_ColInAnnotations("local")
3582                )
3583                secondary_aliasizer = ClauseAdapter(
3584                    dest_selectable, equivalents=self.child_equivalents
3585                ).chain(primary_aliasizer)
3586                if source_selectable is not None:
3587                    primary_aliasizer = ClauseAdapter(
3588                        secondary, exclude_fn=_ColInAnnotations("local")
3589                    ).chain(
3590                        ClauseAdapter(
3591                            source_selectable,
3592                            equivalents=self.parent_equivalents,
3593                        )
3594                    )
3595
3596                secondaryjoin = secondary_aliasizer.traverse(secondaryjoin)
3597            else:
3598                primary_aliasizer = ClauseAdapter(
3599                    dest_selectable,
3600                    exclude_fn=_ColInAnnotations("local"),
3601                    equivalents=self.child_equivalents,
3602                )
3603                if source_selectable is not None:
3604                    primary_aliasizer.chain(
3605                        ClauseAdapter(
3606                            source_selectable,
3607                            exclude_fn=_ColInAnnotations("remote"),
3608                            equivalents=self.parent_equivalents,
3609                        )
3610                    )
3611                secondary_aliasizer = None
3612
3613            primaryjoin = primary_aliasizer.traverse(primaryjoin)
3614            target_adapter = secondary_aliasizer or primary_aliasizer
3615            target_adapter.exclude_fn = None
3616        else:
3617            target_adapter = None
3618        return (
3619            primaryjoin,
3620            secondaryjoin,
3621            secondary,
3622            target_adapter,
3623            dest_selectable,
3624        )
3625
3626    def create_lazy_clause(self, reverse_direction=False):
3627        binds = util.column_dict()
3628        equated_columns = util.column_dict()
3629
3630        has_secondary = self.secondaryjoin is not None
3631
3632        if has_secondary:
3633            lookup = collections.defaultdict(list)
3634            for l, r in self.local_remote_pairs:
3635                lookup[l].append((l, r))
3636                equated_columns[r] = l
3637        elif not reverse_direction:
3638            for l, r in self.local_remote_pairs:
3639                equated_columns[r] = l
3640        else:
3641            for l, r in self.local_remote_pairs:
3642                equated_columns[l] = r
3643
3644        def col_to_bind(col):
3645
3646            if (
3647                (not reverse_direction and "local" in col._annotations)
3648                or reverse_direction
3649                and (
3650                    (has_secondary and col in lookup)
3651                    or (not has_secondary and "remote" in col._annotations)
3652                )
3653            ):
3654                if col not in binds:
3655                    binds[col] = sql.bindparam(
3656                        None, None, type_=col.type, unique=True
3657                    )
3658                return binds[col]
3659            return None
3660
3661        lazywhere = self.primaryjoin
3662        if self.secondaryjoin is None or not reverse_direction:
3663            lazywhere = visitors.replacement_traverse(
3664                lazywhere, {}, col_to_bind
3665            )
3666
3667        if self.secondaryjoin is not None:
3668            secondaryjoin = self.secondaryjoin
3669            if reverse_direction:
3670                secondaryjoin = visitors.replacement_traverse(
3671                    secondaryjoin, {}, col_to_bind
3672                )
3673            lazywhere = sql.and_(lazywhere, secondaryjoin)
3674
3675        bind_to_col = {binds[col].key: col for col in binds}
3676
3677        return lazywhere, bind_to_col, equated_columns
3678
3679
3680class _ColInAnnotations(object):
3681    """Serializable object that tests for a name in c._annotations."""
3682
3683    __slots__ = ("name",)
3684
3685    def __init__(self, name):
3686        self.name = name
3687
3688    def __call__(self, c):
3689        return self.name in c._annotations
3690