1# orm/relationships.py
2# Copyright (C) 2005-2016 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: http://www.opensource.org/licenses/mit-license.php
7
8"""Heuristics related to join conditions as used in
9:func:`.relationship`.
10
11Provides the :class:`.JoinCondition` object, which encapsulates
12SQL annotation and aliasing behavior focused on the `primaryjoin`
13and `secondaryjoin` aspects of :func:`.relationship`.
14
15"""
16from __future__ import absolute_import
17from .. import sql, util, exc as sa_exc, schema, log
18
19import weakref
20from .util import CascadeOptions, _orm_annotate, _orm_deannotate
21from . import dependency
22from . import attributes
23from ..sql.util import (
24    ClauseAdapter,
25    join_condition, _shallow_annotate, visit_binary_product,
26    _deep_deannotate, selectables_overlap, adapt_criterion_to_null
27)
28from ..sql import operators, expression, visitors
29from .interfaces import (MANYTOMANY, MANYTOONE, ONETOMANY,
30                         StrategizedProperty, PropComparator)
31from ..inspection import inspect
32from . import mapper as mapperlib
33import collections
34
35
36def remote(expr):
37    """Annotate a portion of a primaryjoin expression
38    with a 'remote' annotation.
39
40    See the section :ref:`relationship_custom_foreign` for a
41    description of use.
42
43    .. versionadded:: 0.8
44
45    .. seealso::
46
47        :ref:`relationship_custom_foreign`
48
49        :func:`.foreign`
50
51    """
52    return _annotate_columns(expression._clause_element_as_expr(expr),
53                             {"remote": True})
54
55
56def foreign(expr):
57    """Annotate a portion of a primaryjoin expression
58    with a 'foreign' annotation.
59
60    See the section :ref:`relationship_custom_foreign` for a
61    description of use.
62
63    .. versionadded:: 0.8
64
65    .. seealso::
66
67        :ref:`relationship_custom_foreign`
68
69        :func:`.remote`
70
71    """
72
73    return _annotate_columns(expression._clause_element_as_expr(expr),
74                             {"foreign": True})
75
76
77@log.class_logger
78@util.langhelpers.dependency_for("sqlalchemy.orm.properties")
79class RelationshipProperty(StrategizedProperty):
80    """Describes an object property that holds a single item or list
81    of items that correspond to a related database table.
82
83    Public constructor is the :func:`.orm.relationship` function.
84
85    See also:
86
87    :ref:`relationship_config_toplevel`
88
89    """
90
91    strategy_wildcard_key = 'relationship'
92
93    _dependency_processor = None
94
95    def __init__(self, argument,
96                 secondary=None, primaryjoin=None,
97                 secondaryjoin=None,
98                 foreign_keys=None,
99                 uselist=None,
100                 order_by=False,
101                 backref=None,
102                 back_populates=None,
103                 post_update=False,
104                 cascade=False, extension=None,
105                 viewonly=False, lazy=True,
106                 collection_class=None, passive_deletes=False,
107                 passive_updates=True, remote_side=None,
108                 enable_typechecks=True, join_depth=None,
109                 comparator_factory=None,
110                 single_parent=False, innerjoin=False,
111                 distinct_target_key=None,
112                 doc=None,
113                 active_history=False,
114                 cascade_backrefs=True,
115                 load_on_pending=False,
116                 bake_queries=True,
117                 strategy_class=None, _local_remote_pairs=None,
118                 query_class=None,
119                 info=None):
120        """Provide a relationship between two mapped classes.
121
122        This corresponds to a parent-child or associative table relationship.
123        The constructed class is an instance of
124        :class:`.RelationshipProperty`.
125
126        A typical :func:`.relationship`, used in a classical mapping::
127
128           mapper(Parent, properties={
129             'children': relationship(Child)
130           })
131
132        Some arguments accepted by :func:`.relationship` optionally accept a
133        callable function, which when called produces the desired value.
134        The callable is invoked by the parent :class:`.Mapper` at "mapper
135        initialization" time, which happens only when mappers are first used,
136        and is assumed to be after all mappings have been constructed.  This
137        can be used to resolve order-of-declaration and other dependency
138        issues, such as if ``Child`` is declared below ``Parent`` in the same
139        file::
140
141            mapper(Parent, properties={
142                "children":relationship(lambda: Child,
143                                    order_by=lambda: Child.id)
144            })
145
146        When using the :ref:`declarative_toplevel` extension, the Declarative
147        initializer allows string arguments to be passed to
148        :func:`.relationship`.  These string arguments are converted into
149        callables that evaluate the string as Python code, using the
150        Declarative class-registry as a namespace.  This allows the lookup of
151        related classes to be automatic via their string name, and removes the
152        need to import related classes at all into the local module space::
153
154            from sqlalchemy.ext.declarative import declarative_base
155
156            Base = declarative_base()
157
158            class Parent(Base):
159                __tablename__ = 'parent'
160                id = Column(Integer, primary_key=True)
161                children = relationship("Child", order_by="Child.id")
162
163        .. seealso::
164
165          :ref:`relationship_config_toplevel` - Full introductory and
166          reference documentation for :func:`.relationship`.
167
168          :ref:`orm_tutorial_relationship` - ORM tutorial introduction.
169
170        :param argument:
171          a mapped class, or actual :class:`.Mapper` instance, representing
172          the target of the relationship.
173
174          :paramref:`~.relationship.argument` may also be passed as a callable
175          function which is evaluated at mapper initialization time, and may
176          be passed as a Python-evaluable string when using Declarative.
177
178          .. seealso::
179
180            :ref:`declarative_configuring_relationships` - further detail
181            on relationship configuration when using Declarative.
182
183        :param secondary:
184          for a many-to-many relationship, specifies the intermediary
185          table, and is typically an instance of :class:`.Table`.
186          In less common circumstances, the argument may also be specified
187          as an :class:`.Alias` construct, or even a :class:`.Join` construct.
188
189          :paramref:`~.relationship.secondary` may
190          also be passed as a callable function which is evaluated at
191          mapper initialization time.  When using Declarative, it may also
192          be a string argument noting the name of a :class:`.Table` that is
193          present in the :class:`.MetaData` collection associated with the
194          parent-mapped :class:`.Table`.
195
196          The :paramref:`~.relationship.secondary` keyword argument is
197          typically applied in the case where the intermediary :class:`.Table`
198          is not otherwise expressed in any direct class mapping. If the
199          "secondary" table is also explicitly mapped elsewhere (e.g. as in
200          :ref:`association_pattern`), one should consider applying the
201          :paramref:`~.relationship.viewonly` flag so that this
202          :func:`.relationship` is not used for persistence operations which
203          may conflict with those of the association object pattern.
204
205          .. seealso::
206
207              :ref:`relationships_many_to_many` - Reference example of "many
208              to many".
209
210              :ref:`orm_tutorial_many_to_many` - ORM tutorial introduction to
211              many-to-many relationships.
212
213              :ref:`self_referential_many_to_many` - Specifics on using
214              many-to-many in a self-referential case.
215
216              :ref:`declarative_many_to_many` - Additional options when using
217              Declarative.
218
219              :ref:`association_pattern` - an alternative to
220              :paramref:`~.relationship.secondary` when composing association
221              table relationships, allowing additional attributes to be
222              specified on the association table.
223
224              :ref:`composite_secondary_join` - a lesser-used pattern which
225              in some cases can enable complex :func:`.relationship` SQL
226              conditions to be used.
227
228          .. versionadded:: 0.9.2 :paramref:`~.relationship.secondary` works
229             more effectively when referring to a :class:`.Join` instance.
230
231        :param active_history=False:
232          When ``True``, indicates that the "previous" value for a
233          many-to-one reference should be loaded when replaced, if
234          not already loaded. Normally, history tracking logic for
235          simple many-to-ones only needs to be aware of the "new"
236          value in order to perform a flush. This flag is available
237          for applications that make use of
238          :func:`.attributes.get_history` which also need to know
239          the "previous" value of the attribute.
240
241        :param backref:
242          indicates the string name of a property to be placed on the related
243          mapper's class that will handle this relationship in the other
244          direction. The other property will be created automatically
245          when the mappers are configured.  Can also be passed as a
246          :func:`.backref` object to control the configuration of the
247          new relationship.
248
249          .. seealso::
250
251            :ref:`relationships_backref` - Introductory documentation and
252            examples.
253
254            :paramref:`~.relationship.back_populates` - alternative form
255            of backref specification.
256
257            :func:`.backref` - allows control over :func:`.relationship`
258            configuration when using :paramref:`~.relationship.backref`.
259
260
261        :param back_populates:
262          Takes a string name and has the same meaning as
263          :paramref:`~.relationship.backref`, except the complementing
264          property is **not** created automatically, and instead must be
265          configured explicitly on the other mapper.  The complementing
266          property should also indicate
267          :paramref:`~.relationship.back_populates` to this relationship to
268          ensure proper functioning.
269
270          .. seealso::
271
272            :ref:`relationships_backref` - Introductory documentation and
273            examples.
274
275            :paramref:`~.relationship.backref` - alternative form
276            of backref specification.
277
278        :param bake_queries=True:
279          Use the :class:`.BakedQuery` cache to cache the construction of SQL
280          used in lazy loads, when the :func:`.bake_lazy_loaders` function has
281          first been called.  Defaults to True and is intended to provide an
282          "opt out" flag per-relationship when the baked query cache system is
283          in use.
284
285          .. warning::
286
287              This flag **only** has an effect when the application-wide
288              :func:`.bake_lazy_loaders` function has been called.   It
289              defaults to True so is an "opt out" flag.
290
291          Setting this flag to False when baked queries are otherwise in
292          use might be to reduce
293          ORM memory use for this :func:`.relationship`, or to work around
294          unresolved stability issues observed within the baked query
295          cache system.
296
297          .. versionadded:: 1.0.0
298
299          .. seealso::
300
301            :ref:`baked_toplevel`
302
303        :param cascade:
304          a comma-separated list of cascade rules which determines how
305          Session operations should be "cascaded" from parent to child.
306          This defaults to ``False``, which means the default cascade
307          should be used - this default cascade is ``"save-update, merge"``.
308
309          The available cascades are ``save-update``, ``merge``,
310          ``expunge``, ``delete``, ``delete-orphan``, and ``refresh-expire``.
311          An additional option, ``all`` indicates shorthand for
312          ``"save-update, merge, refresh-expire,
313          expunge, delete"``, and is often used as in ``"all, delete-orphan"``
314          to indicate that related objects should follow along with the
315          parent object in all cases, and be deleted when de-associated.
316
317          .. seealso::
318
319            :ref:`unitofwork_cascades` - Full detail on each of the available
320            cascade options.
321
322            :ref:`tutorial_delete_cascade` - Tutorial example describing
323            a delete cascade.
324
325        :param cascade_backrefs=True:
326          a boolean value indicating if the ``save-update`` cascade should
327          operate along an assignment event intercepted by a backref.
328          When set to ``False``, the attribute managed by this relationship
329          will not cascade an incoming transient object into the session of a
330          persistent parent, if the event is received via backref.
331
332          .. seealso::
333
334            :ref:`backref_cascade` - Full discussion and examples on how
335            the :paramref:`~.relationship.cascade_backrefs` option is used.
336
337        :param collection_class:
338          a class or callable that returns a new list-holding object. will
339          be used in place of a plain list for storing elements.
340
341          .. seealso::
342
343            :ref:`custom_collections` - Introductory documentation and
344            examples.
345
346        :param comparator_factory:
347          a class which extends :class:`.RelationshipProperty.Comparator`
348          which provides custom SQL clause generation for comparison
349          operations.
350
351          .. seealso::
352
353            :class:`.PropComparator` - some detail on redefining comparators
354            at this level.
355
356            :ref:`custom_comparators` - Brief intro to this feature.
357
358
359        :param distinct_target_key=None:
360          Indicate if a "subquery" eager load should apply the DISTINCT
361          keyword to the innermost SELECT statement.  When left as ``None``,
362          the DISTINCT keyword will be applied in those cases when the target
363          columns do not comprise the full primary key of the target table.
364          When set to ``True``, the DISTINCT keyword is applied to the
365          innermost SELECT unconditionally.
366
367          It may be desirable to set this flag to False when the DISTINCT is
368          reducing performance of the innermost subquery beyond that of what
369          duplicate innermost rows may be causing.
370
371          .. versionadded:: 0.8.3 -
372             :paramref:`~.relationship.distinct_target_key` allows the
373             subquery eager loader to apply a DISTINCT modifier to the
374             innermost SELECT.
375
376          .. versionchanged:: 0.9.0 -
377             :paramref:`~.relationship.distinct_target_key` now defaults to
378             ``None``, so that the feature enables itself automatically for
379             those cases where the innermost query targets a non-unique
380             key.
381
382          .. seealso::
383
384            :ref:`loading_toplevel` - includes an introduction to subquery
385            eager loading.
386
387        :param doc:
388          docstring which will be applied to the resulting descriptor.
389
390        :param extension:
391          an :class:`.AttributeExtension` instance, or list of extensions,
392          which will be prepended to the list of attribute listeners for
393          the resulting descriptor placed on the class.
394
395          .. deprecated:: 0.7 Please see :class:`.AttributeEvents`.
396
397        :param foreign_keys:
398
399          a list of columns which are to be used as "foreign key"
400          columns, or columns which refer to the value in a remote
401          column, within the context of this :func:`.relationship`
402          object's :paramref:`~.relationship.primaryjoin` condition.
403          That is, if the :paramref:`~.relationship.primaryjoin`
404          condition of this :func:`.relationship` is ``a.id ==
405          b.a_id``, and the values in ``b.a_id`` are required to be
406          present in ``a.id``, then the "foreign key" column of this
407          :func:`.relationship` is ``b.a_id``.
408
409          In normal cases, the :paramref:`~.relationship.foreign_keys`
410          parameter is **not required.** :func:`.relationship` will
411          automatically determine which columns in the
412          :paramref:`~.relationship.primaryjoin` conditition are to be
413          considered "foreign key" columns based on those
414          :class:`.Column` objects that specify :class:`.ForeignKey`,
415          or are otherwise listed as referencing columns in a
416          :class:`.ForeignKeyConstraint` construct.
417          :paramref:`~.relationship.foreign_keys` is only needed when:
418
419            1. There is more than one way to construct a join from the local
420               table to the remote table, as there are multiple foreign key
421               references present.  Setting ``foreign_keys`` will limit the
422               :func:`.relationship` to consider just those columns specified
423               here as "foreign".
424
425               .. versionchanged:: 0.8
426                    A multiple-foreign key join ambiguity can be resolved by
427                    setting the :paramref:`~.relationship.foreign_keys`
428                    parameter alone, without the need to explicitly set
429                    :paramref:`~.relationship.primaryjoin` as well.
430
431            2. The :class:`.Table` being mapped does not actually have
432               :class:`.ForeignKey` or :class:`.ForeignKeyConstraint`
433               constructs present, often because the table
434               was reflected from a database that does not support foreign key
435               reflection (MySQL MyISAM).
436
437            3. The :paramref:`~.relationship.primaryjoin` argument is used to
438               construct a non-standard join condition, which makes use of
439               columns or expressions that do not normally refer to their
440               "parent" column, such as a join condition expressed by a
441               complex comparison using a SQL function.
442
443          The :func:`.relationship` construct will raise informative
444          error messages that suggest the use of the
445          :paramref:`~.relationship.foreign_keys` parameter when
446          presented with an ambiguous condition.   In typical cases,
447          if :func:`.relationship` doesn't raise any exceptions, the
448          :paramref:`~.relationship.foreign_keys` parameter is usually
449          not needed.
450
451          :paramref:`~.relationship.foreign_keys` may also be passed as a
452          callable function which is evaluated at mapper initialization time,
453          and may be passed as a Python-evaluable string when using
454          Declarative.
455
456          .. seealso::
457
458            :ref:`relationship_foreign_keys`
459
460            :ref:`relationship_custom_foreign`
461
462            :func:`.foreign` - allows direct annotation of the "foreign"
463            columns within a :paramref:`~.relationship.primaryjoin` condition.
464
465          .. versionadded:: 0.8
466              The :func:`.foreign` annotation can also be applied
467              directly to the :paramref:`~.relationship.primaryjoin`
468              expression, which is an alternate, more specific system of
469              describing which columns in a particular
470              :paramref:`~.relationship.primaryjoin` should be considered
471              "foreign".
472
473        :param info: Optional data dictionary which will be populated into the
474            :attr:`.MapperProperty.info` attribute of this object.
475
476            .. versionadded:: 0.8
477
478        :param innerjoin=False:
479          when ``True``, joined eager loads will use an inner join to join
480          against related tables instead of an outer join.  The purpose
481          of this option is generally one of performance, as inner joins
482          generally perform better than outer joins.
483
484          This flag can be set to ``True`` when the relationship references an
485          object via many-to-one using local foreign keys that are not
486          nullable, or when the reference is one-to-one or a collection that
487          is guaranteed to have one or at least one entry.
488
489          The option supports the same "nested" and "unnested" options as
490          that of :paramref:`.joinedload.innerjoin`.  See that flag
491          for details on nested / unnested behaviors.
492
493          .. seealso::
494
495            :paramref:`.joinedload.innerjoin` - the option as specified by
496            loader option, including detail on nesting behavior.
497
498            :ref:`what_kind_of_loading` - Discussion of some details of
499            various loader options.
500
501
502        :param join_depth:
503          when non-``None``, an integer value indicating how many levels
504          deep "eager" loaders should join on a self-referring or cyclical
505          relationship.  The number counts how many times the same Mapper
506          shall be present in the loading condition along a particular join
507          branch.  When left at its default of ``None``, eager loaders
508          will stop chaining when they encounter a the same target mapper
509          which is already higher up in the chain.  This option applies
510          both to joined- and subquery- eager loaders.
511
512          .. seealso::
513
514            :ref:`self_referential_eager_loading` - Introductory documentation
515            and examples.
516
517        :param lazy='select': specifies
518          how the related items should be loaded.  Default value is
519          ``select``.  Values include:
520
521          * ``select`` - items should be loaded lazily when the property is
522            first accessed, using a separate SELECT statement, or identity map
523            fetch for simple many-to-one references.
524
525          * ``immediate`` - items should be loaded as the parents are loaded,
526            using a separate SELECT statement, or identity map fetch for
527            simple many-to-one references.
528
529          * ``joined`` - items should be loaded "eagerly" in the same query as
530            that of the parent, using a JOIN or LEFT OUTER JOIN.  Whether
531            the join is "outer" or not is determined by the
532            :paramref:`~.relationship.innerjoin` parameter.
533
534          * ``subquery`` - items should be loaded "eagerly" as the parents are
535            loaded, using one additional SQL statement, which issues a JOIN to
536            a subquery of the original statement, for each collection
537            requested.
538
539          * ``noload`` - no loading should occur at any time.  This is to
540            support "write-only" attributes, or attributes which are
541            populated in some manner specific to the application.
542
543          * ``dynamic`` - the attribute will return a pre-configured
544            :class:`.Query` object for all read
545            operations, onto which further filtering operations can be
546            applied before iterating the results.  See
547            the section :ref:`dynamic_relationship` for more details.
548
549          * True - a synonym for 'select'
550
551          * False - a synonym for 'joined'
552
553          * None - a synonym for 'noload'
554
555          .. seealso::
556
557            :doc:`/orm/loading_relationships` - Full documentation on relationship loader
558            configuration.
559
560            :ref:`dynamic_relationship` - detail on the ``dynamic`` option.
561
562        :param load_on_pending=False:
563          Indicates loading behavior for transient or pending parent objects.
564
565          When set to ``True``, causes the lazy-loader to
566          issue a query for a parent object that is not persistent, meaning it
567          has never been flushed.  This may take effect for a pending object
568          when autoflush is disabled, or for a transient object that has been
569          "attached" to a :class:`.Session` but is not part of its pending
570          collection.
571
572          The :paramref:`~.relationship.load_on_pending` flag does not improve
573          behavior when the ORM is used normally - object references should be
574          constructed at the object level, not at the foreign key level, so
575          that they are present in an ordinary way before a flush proceeds.
576          This flag is not not intended for general use.
577
578          .. seealso::
579
580              :meth:`.Session.enable_relationship_loading` - this method
581              establishes "load on pending" behavior for the whole object, and
582              also allows loading on objects that remain transient or
583              detached.
584
585        :param order_by:
586          indicates the ordering that should be applied when loading these
587          items.  :paramref:`~.relationship.order_by` is expected to refer to
588          one of the :class:`.Column` objects to which the target class is
589          mapped, or the attribute itself bound to the target class which
590          refers to the column.
591
592          :paramref:`~.relationship.order_by` may also be passed as a callable
593          function which is evaluated at mapper initialization time, and may
594          be passed as a Python-evaluable string when using Declarative.
595
596        :param passive_deletes=False:
597           Indicates loading behavior during delete operations.
598
599           A value of True indicates that unloaded child items should not
600           be loaded during a delete operation on the parent.  Normally,
601           when a parent item is deleted, all child items are loaded so
602           that they can either be marked as deleted, or have their
603           foreign key to the parent set to NULL.  Marking this flag as
604           True usually implies an ON DELETE <CASCADE|SET NULL> rule is in
605           place which will handle updating/deleting child rows on the
606           database side.
607
608           Additionally, setting the flag to the string value 'all' will
609           disable the "nulling out" of the child foreign keys, when there
610           is no delete or delete-orphan cascade enabled.  This is
611           typically used when a triggering or error raise scenario is in
612           place on the database side.  Note that the foreign key
613           attributes on in-session child objects will not be changed
614           after a flush occurs so this is a very special use-case
615           setting.
616
617           .. seealso::
618
619                :ref:`passive_deletes` - Introductory documentation
620                and examples.
621
622        :param passive_updates=True:
623          Indicates the persistence behavior to take when a referenced
624          primary key value changes in place, indicating that the referencing
625          foreign key columns will also need their value changed.
626
627          When True, it is assumed that ``ON UPDATE CASCADE`` is configured on
628          the foreign key in the database, and that the database will
629          handle propagation of an UPDATE from a source column to
630          dependent rows.  When False, the SQLAlchemy :func:`.relationship`
631          construct will attempt to emit its own UPDATE statements to
632          modify related targets.  However note that SQLAlchemy **cannot**
633          emit an UPDATE for more than one level of cascade.  Also,
634          setting this flag to False is not compatible in the case where
635          the database is in fact enforcing referential integrity, unless
636          those constraints are explicitly "deferred", if the target backend
637          supports it.
638
639          It is highly advised that an application which is employing
640          mutable primary keys keeps ``passive_updates`` set to True,
641          and instead uses the referential integrity features of the database
642          itself in order to handle the change efficiently and fully.
643
644          .. seealso::
645
646              :ref:`passive_updates` - Introductory documentation and
647              examples.
648
649              :paramref:`.mapper.passive_updates` - a similar flag which
650              takes effect for joined-table inheritance mappings.
651
652        :param post_update:
653          this indicates that the relationship should be handled by a
654          second UPDATE statement after an INSERT or before a
655          DELETE. Currently, it also will issue an UPDATE after the
656          instance was UPDATEd as well, although this technically should
657          be improved. This flag is used to handle saving bi-directional
658          dependencies between two individual rows (i.e. each row
659          references the other), where it would otherwise be impossible to
660          INSERT or DELETE both rows fully since one row exists before the
661          other. Use this flag when a particular mapping arrangement will
662          incur two rows that are dependent on each other, such as a table
663          that has a one-to-many relationship to a set of child rows, and
664          also has a column that references a single child row within that
665          list (i.e. both tables contain a foreign key to each other). If
666          a flush operation returns an error that a "cyclical
667          dependency" was detected, this is a cue that you might want to
668          use :paramref:`~.relationship.post_update` to "break" the cycle.
669
670          .. seealso::
671
672              :ref:`post_update` - Introductory documentation and examples.
673
674        :param primaryjoin:
675          a SQL expression that will be used as the primary
676          join of this child object against the parent object, or in a
677          many-to-many relationship the join of the primary object to the
678          association table. By default, this value is computed based on the
679          foreign key relationships of the parent and child tables (or
680          association table).
681
682          :paramref:`~.relationship.primaryjoin` may also be passed as a
683          callable function which is evaluated at mapper initialization time,
684          and may be passed as a Python-evaluable string when using
685          Declarative.
686
687          .. seealso::
688
689              :ref:`relationship_primaryjoin`
690
691        :param remote_side:
692          used for self-referential relationships, indicates the column or
693          list of columns that form the "remote side" of the relationship.
694
695          :paramref:`.relationship.remote_side` may also be passed as a
696          callable function which is evaluated at mapper initialization time,
697          and may be passed as a Python-evaluable string when using
698          Declarative.
699
700          .. versionchanged:: 0.8
701              The :func:`.remote` annotation can also be applied
702              directly to the ``primaryjoin`` expression, which is an
703              alternate, more specific system of describing which columns in a
704              particular ``primaryjoin`` should be considered "remote".
705
706          .. seealso::
707
708            :ref:`self_referential` - in-depth explanation of how
709            :paramref:`~.relationship.remote_side`
710            is used to configure self-referential relationships.
711
712            :func:`.remote` - an annotation function that accomplishes the
713            same purpose as :paramref:`~.relationship.remote_side`, typically
714            when a custom :paramref:`~.relationship.primaryjoin` condition
715            is used.
716
717        :param query_class:
718          a :class:`.Query` subclass that will be used as the base of the
719          "appender query" returned by a "dynamic" relationship, that
720          is, a relationship that specifies ``lazy="dynamic"`` or was
721          otherwise constructed using the :func:`.orm.dynamic_loader`
722          function.
723
724          .. seealso::
725
726            :ref:`dynamic_relationship` - Introduction to "dynamic"
727            relationship loaders.
728
729        :param secondaryjoin:
730          a SQL expression that will be used as the join of
731          an association table to the child object. By default, this value is
732          computed based on the foreign key relationships of the association
733          and child tables.
734
735          :paramref:`~.relationship.secondaryjoin` may also be passed as a
736          callable function which is evaluated at mapper initialization time,
737          and may be passed as a Python-evaluable string when using
738          Declarative.
739
740          .. seealso::
741
742              :ref:`relationship_primaryjoin`
743
744        :param single_parent:
745          when True, installs a validator which will prevent objects
746          from being associated with more than one parent at a time.
747          This is used for many-to-one or many-to-many relationships that
748          should be treated either as one-to-one or one-to-many.  Its usage
749          is optional, except for :func:`.relationship` constructs which
750          are many-to-one or many-to-many and also
751          specify the ``delete-orphan`` cascade option.  The
752          :func:`.relationship` construct itself will raise an error
753          instructing when this option is required.
754
755          .. seealso::
756
757            :ref:`unitofwork_cascades` - includes detail on when the
758            :paramref:`~.relationship.single_parent` flag may be appropriate.
759
760        :param uselist:
761          a boolean that indicates if this property should be loaded as a
762          list or a scalar. In most cases, this value is determined
763          automatically by :func:`.relationship` at mapper configuration
764          time, based on the type and direction
765          of the relationship - one to many forms a list, many to one
766          forms a scalar, many to many is a list. If a scalar is desired
767          where normally a list would be present, such as a bi-directional
768          one-to-one relationship, set :paramref:`~.relationship.uselist` to
769          False.
770
771          The :paramref:`~.relationship.uselist` flag is also available on an
772          existing :func:`.relationship` construct as a read-only attribute,
773          which can be used to determine if this :func:`.relationship` deals
774          with collections or scalar attributes::
775
776              >>> User.addresses.property.uselist
777              True
778
779          .. seealso::
780
781              :ref:`relationships_one_to_one` - Introduction to the "one to
782              one" relationship pattern, which is typically when the
783              :paramref:`~.relationship.uselist` flag is needed.
784
785        :param viewonly=False:
786          when set to True, the relationship is used only for loading objects,
787          and not for any persistence operation.  A :func:`.relationship`
788          which specifies :paramref:`~.relationship.viewonly` can work
789          with a wider range of SQL operations within the
790          :paramref:`~.relationship.primaryjoin` condition, including
791          operations that feature the use of a variety of comparison operators
792          as well as SQL functions such as :func:`~.sql.expression.cast`.  The
793          :paramref:`~.relationship.viewonly` flag is also of general use when
794          defining any kind of :func:`~.relationship` that doesn't represent
795          the full set of related objects, to prevent modifications of the
796          collection from resulting in persistence operations.
797
798
799        """
800        super(RelationshipProperty, self).__init__()
801
802        self.uselist = uselist
803        self.argument = argument
804        self.secondary = secondary
805        self.primaryjoin = primaryjoin
806        self.secondaryjoin = secondaryjoin
807        self.post_update = post_update
808        self.direction = None
809        self.viewonly = viewonly
810        self.lazy = lazy
811        self.single_parent = single_parent
812        self._user_defined_foreign_keys = foreign_keys
813        self.collection_class = collection_class
814        self.passive_deletes = passive_deletes
815        self.cascade_backrefs = cascade_backrefs
816        self.passive_updates = passive_updates
817        self.remote_side = remote_side
818        self.enable_typechecks = enable_typechecks
819        self.query_class = query_class
820        self.innerjoin = innerjoin
821        self.distinct_target_key = distinct_target_key
822        self.doc = doc
823        self.active_history = active_history
824        self.join_depth = join_depth
825        self.local_remote_pairs = _local_remote_pairs
826        self.extension = extension
827        self.bake_queries = bake_queries
828        self.load_on_pending = load_on_pending
829        self.comparator_factory = comparator_factory or \
830            RelationshipProperty.Comparator
831        self.comparator = self.comparator_factory(self, None)
832        util.set_creation_order(self)
833
834        if info is not None:
835            self.info = info
836
837        if strategy_class:
838            self.strategy_class = strategy_class
839        else:
840            self.strategy_class = self._strategy_lookup(("lazy", self.lazy))
841
842        self._reverse_property = set()
843
844        self.cascade = cascade if cascade is not False \
845            else "save-update, merge"
846
847        self.order_by = order_by
848
849        self.back_populates = back_populates
850
851        if self.back_populates:
852            if backref:
853                raise sa_exc.ArgumentError(
854                    "backref and back_populates keyword arguments "
855                    "are mutually exclusive")
856            self.backref = None
857        else:
858            self.backref = backref
859
860    def instrument_class(self, mapper):
861        attributes.register_descriptor(
862            mapper.class_,
863            self.key,
864            comparator=self.comparator_factory(self, mapper),
865            parententity=mapper,
866            doc=self.doc,
867        )
868
869    class Comparator(PropComparator):
870        """Produce boolean, comparison, and other operators for
871        :class:`.RelationshipProperty` attributes.
872
873        See the documentation for :class:`.PropComparator` for a brief
874        overview of ORM level operator definition.
875
876        See also:
877
878        :class:`.PropComparator`
879
880        :class:`.ColumnProperty.Comparator`
881
882        :class:`.ColumnOperators`
883
884        :ref:`types_operators`
885
886        :attr:`.TypeEngine.comparator_factory`
887
888        """
889
890        _of_type = None
891
892        def __init__(
893                self, prop, parentmapper, adapt_to_entity=None, of_type=None):
894            """Construction of :class:`.RelationshipProperty.Comparator`
895            is internal to the ORM's attribute mechanics.
896
897            """
898            self.prop = prop
899            self._parententity = parentmapper
900            self._adapt_to_entity = adapt_to_entity
901            if of_type:
902                self._of_type = of_type
903
904        def adapt_to_entity(self, adapt_to_entity):
905            return self.__class__(self.property, self._parententity,
906                                  adapt_to_entity=adapt_to_entity,
907                                  of_type=self._of_type)
908
909        @util.memoized_property
910        def mapper(self):
911            """The target :class:`.Mapper` referred to by this
912            :class:`.RelationshipProperty.Comparator`.
913
914            This is the "target" or "remote" side of the
915            :func:`.relationship`.
916
917            """
918            return self.property.mapper
919
920        @util.memoized_property
921        def _parententity(self):
922            return self.property.parent
923
924        def _source_selectable(self):
925            if self._adapt_to_entity:
926                return self._adapt_to_entity.selectable
927            else:
928                return self.property.parent._with_polymorphic_selectable
929
930        def __clause_element__(self):
931            adapt_from = self._source_selectable()
932            if self._of_type:
933                of_type = inspect(self._of_type).mapper
934            else:
935                of_type = None
936
937            pj, sj, source, dest, \
938                secondary, target_adapter = self.property._create_joins(
939                    source_selectable=adapt_from,
940                    source_polymorphic=True,
941                    of_type=of_type)
942            if sj is not None:
943                return pj & sj
944            else:
945                return pj
946
947        def of_type(self, cls):
948            """Produce a construct that represents a particular 'subtype' of
949            attribute for the parent class.
950
951            Currently this is usable in conjunction with :meth:`.Query.join`
952            and :meth:`.Query.outerjoin`.
953
954            """
955            return RelationshipProperty.Comparator(
956                self.property,
957                self._parententity,
958                adapt_to_entity=self._adapt_to_entity,
959                of_type=cls)
960
961        def in_(self, other):
962            """Produce an IN clause - this is not implemented
963            for :func:`~.orm.relationship`-based attributes at this time.
964
965            """
966            raise NotImplementedError('in_() not yet supported for '
967                                      'relationships.  For a simple '
968                                      'many-to-one, use in_() against '
969                                      'the set of foreign key values.')
970
971        __hash__ = None
972
973        def __eq__(self, other):
974            """Implement the ``==`` operator.
975
976            In a many-to-one context, such as::
977
978              MyClass.some_prop == <some object>
979
980            this will typically produce a
981            clause such as::
982
983              mytable.related_id == <some id>
984
985            Where ``<some id>`` is the primary key of the given
986            object.
987
988            The ``==`` operator provides partial functionality for non-
989            many-to-one comparisons:
990
991            * Comparisons against collections are not supported.
992              Use :meth:`~.RelationshipProperty.Comparator.contains`.
993            * Compared to a scalar one-to-many, will produce a
994              clause that compares the target columns in the parent to
995              the given target.
996            * Compared to a scalar many-to-many, an alias
997              of the association table will be rendered as
998              well, forming a natural join that is part of the
999              main body of the query. This will not work for
1000              queries that go beyond simple AND conjunctions of
1001              comparisons, such as those which use OR. Use
1002              explicit joins, outerjoins, or
1003              :meth:`~.RelationshipProperty.Comparator.has` for
1004              more comprehensive non-many-to-one scalar
1005              membership tests.
1006            * Comparisons against ``None`` given in a one-to-many
1007              or many-to-many context produce a NOT EXISTS clause.
1008
1009            """
1010            if isinstance(other, (util.NoneType, expression.Null)):
1011                if self.property.direction in [ONETOMANY, MANYTOMANY]:
1012                    return ~self._criterion_exists()
1013                else:
1014                    return _orm_annotate(self.property._optimized_compare(
1015                        None, adapt_source=self.adapter))
1016            elif self.property.uselist:
1017                raise sa_exc.InvalidRequestError(
1018                    "Can't compare a collection to an object or collection; "
1019                    "use contains() to test for membership.")
1020            else:
1021                return _orm_annotate(
1022                    self.property._optimized_compare(
1023                        other, adapt_source=self.adapter))
1024
1025        def _criterion_exists(self, criterion=None, **kwargs):
1026            if getattr(self, '_of_type', None):
1027                info = inspect(self._of_type)
1028                target_mapper, to_selectable, is_aliased_class = \
1029                    info.mapper, info.selectable, info.is_aliased_class
1030                if self.property._is_self_referential and not \
1031                        is_aliased_class:
1032                    to_selectable = to_selectable.alias()
1033
1034                single_crit = target_mapper._single_table_criterion
1035                if single_crit is not None:
1036                    if criterion is not None:
1037                        criterion = single_crit & criterion
1038                    else:
1039                        criterion = single_crit
1040            else:
1041                is_aliased_class = False
1042                to_selectable = None
1043
1044            if self.adapter:
1045                source_selectable = self._source_selectable()
1046            else:
1047                source_selectable = None
1048
1049            pj, sj, source, dest, secondary, target_adapter = \
1050                self.property._create_joins(
1051                    dest_polymorphic=True,
1052                    dest_selectable=to_selectable,
1053                    source_selectable=source_selectable)
1054
1055            for k in kwargs:
1056                crit = getattr(self.property.mapper.class_, k) == kwargs[k]
1057                if criterion is None:
1058                    criterion = crit
1059                else:
1060                    criterion = criterion & crit
1061
1062            # annotate the *local* side of the join condition, in the case
1063            # of pj + sj this is the full primaryjoin, in the case of just
1064            # pj its the local side of the primaryjoin.
1065            if sj is not None:
1066                j = _orm_annotate(pj) & sj
1067            else:
1068                j = _orm_annotate(pj, exclude=self.property.remote_side)
1069
1070            if criterion is not None and target_adapter and not \
1071                    is_aliased_class:
1072                # limit this adapter to annotated only?
1073                criterion = target_adapter.traverse(criterion)
1074
1075            # only have the "joined left side" of what we
1076            # return be subject to Query adaption.  The right
1077            # side of it is used for an exists() subquery and
1078            # should not correlate or otherwise reach out
1079            # to anything in the enclosing query.
1080            if criterion is not None:
1081                criterion = criterion._annotate(
1082                    {'no_replacement_traverse': True})
1083
1084            crit = j & sql.True_._ifnone(criterion)
1085
1086            ex = sql.exists([1], crit, from_obj=dest).correlate_except(dest)
1087            if secondary is not None:
1088                ex = ex.correlate_except(secondary)
1089            return ex
1090
1091        def any(self, criterion=None, **kwargs):
1092            """Produce an expression that tests a collection against
1093            particular criterion, using EXISTS.
1094
1095            An expression like::
1096
1097                session.query(MyClass).filter(
1098                    MyClass.somereference.any(SomeRelated.x==2)
1099                )
1100
1101
1102            Will produce a query like::
1103
1104                SELECT * FROM my_table WHERE
1105                EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id
1106                AND related.x=2)
1107
1108            Because :meth:`~.RelationshipProperty.Comparator.any` uses
1109            a correlated subquery, its performance is not nearly as
1110            good when compared against large target tables as that of
1111            using a join.
1112
1113            :meth:`~.RelationshipProperty.Comparator.any` is particularly
1114            useful for testing for empty collections::
1115
1116                session.query(MyClass).filter(
1117                    ~MyClass.somereference.any()
1118                )
1119
1120            will produce::
1121
1122                SELECT * FROM my_table WHERE
1123                NOT EXISTS (SELECT 1 FROM related WHERE
1124                related.my_id=my_table.id)
1125
1126            :meth:`~.RelationshipProperty.Comparator.any` is only
1127            valid for collections, i.e. a :func:`.relationship`
1128            that has ``uselist=True``.  For scalar references,
1129            use :meth:`~.RelationshipProperty.Comparator.has`.
1130
1131            """
1132            if not self.property.uselist:
1133                raise sa_exc.InvalidRequestError(
1134                    "'any()' not implemented for scalar "
1135                    "attributes. Use has()."
1136                )
1137
1138            return self._criterion_exists(criterion, **kwargs)
1139
1140        def has(self, criterion=None, **kwargs):
1141            """Produce an expression that tests a scalar reference against
1142            particular criterion, using EXISTS.
1143
1144            An expression like::
1145
1146                session.query(MyClass).filter(
1147                    MyClass.somereference.has(SomeRelated.x==2)
1148                )
1149
1150
1151            Will produce a query like::
1152
1153                SELECT * FROM my_table WHERE
1154                EXISTS (SELECT 1 FROM related WHERE
1155                related.id==my_table.related_id AND related.x=2)
1156
1157            Because :meth:`~.RelationshipProperty.Comparator.has` uses
1158            a correlated subquery, its performance is not nearly as
1159            good when compared against large target tables as that of
1160            using a join.
1161
1162            :meth:`~.RelationshipProperty.Comparator.has` is only
1163            valid for scalar references, i.e. a :func:`.relationship`
1164            that has ``uselist=False``.  For collection references,
1165            use :meth:`~.RelationshipProperty.Comparator.any`.
1166
1167            """
1168            if self.property.uselist:
1169                raise sa_exc.InvalidRequestError(
1170                    "'has()' not implemented for collections.  "
1171                    "Use any().")
1172            return self._criterion_exists(criterion, **kwargs)
1173
1174        def contains(self, other, **kwargs):
1175            """Return a simple expression that tests a collection for
1176            containment of a particular item.
1177
1178            :meth:`~.RelationshipProperty.Comparator.contains` is
1179            only valid for a collection, i.e. a
1180            :func:`~.orm.relationship` that implements
1181            one-to-many or many-to-many with ``uselist=True``.
1182
1183            When used in a simple one-to-many context, an
1184            expression like::
1185
1186                MyClass.contains(other)
1187
1188            Produces a clause like::
1189
1190                mytable.id == <some id>
1191
1192            Where ``<some id>`` is the value of the foreign key
1193            attribute on ``other`` which refers to the primary
1194            key of its parent object. From this it follows that
1195            :meth:`~.RelationshipProperty.Comparator.contains` is
1196            very useful when used with simple one-to-many
1197            operations.
1198
1199            For many-to-many operations, the behavior of
1200            :meth:`~.RelationshipProperty.Comparator.contains`
1201            has more caveats. The association table will be
1202            rendered in the statement, producing an "implicit"
1203            join, that is, includes multiple tables in the FROM
1204            clause which are equated in the WHERE clause::
1205
1206                query(MyClass).filter(MyClass.contains(other))
1207
1208            Produces a query like::
1209
1210                SELECT * FROM my_table, my_association_table AS
1211                my_association_table_1 WHERE
1212                my_table.id = my_association_table_1.parent_id
1213                AND my_association_table_1.child_id = <some id>
1214
1215            Where ``<some id>`` would be the primary key of
1216            ``other``. From the above, it is clear that
1217            :meth:`~.RelationshipProperty.Comparator.contains`
1218            will **not** work with many-to-many collections when
1219            used in queries that move beyond simple AND
1220            conjunctions, such as multiple
1221            :meth:`~.RelationshipProperty.Comparator.contains`
1222            expressions joined by OR. In such cases subqueries or
1223            explicit "outer joins" will need to be used instead.
1224            See :meth:`~.RelationshipProperty.Comparator.any` for
1225            a less-performant alternative using EXISTS, or refer
1226            to :meth:`.Query.outerjoin` as well as :ref:`ormtutorial_joins`
1227            for more details on constructing outer joins.
1228
1229            """
1230            if not self.property.uselist:
1231                raise sa_exc.InvalidRequestError(
1232                    "'contains' not implemented for scalar "
1233                    "attributes.  Use ==")
1234            clause = self.property._optimized_compare(
1235                other, adapt_source=self.adapter)
1236
1237            if self.property.secondaryjoin is not None:
1238                clause.negation_clause = \
1239                    self.__negated_contains_or_equals(other)
1240
1241            return clause
1242
1243        def __negated_contains_or_equals(self, other):
1244            if self.property.direction == MANYTOONE:
1245                state = attributes.instance_state(other)
1246
1247                def state_bindparam(x, state, col):
1248                    dict_ = state.dict
1249                    return sql.bindparam(
1250                        x, unique=True,
1251                        callable_=self.property._get_attr_w_warn_on_none(
1252                            col,
1253                            self.property.mapper._get_state_attr_by_column,
1254                            state, dict_, col, passive=attributes.PASSIVE_OFF
1255                        )
1256                    )
1257
1258                def adapt(col):
1259                    if self.adapter:
1260                        return self.adapter(col)
1261                    else:
1262                        return col
1263
1264                if self.property._use_get:
1265                    return sql.and_(*[
1266                        sql.or_(
1267                            adapt(x) != state_bindparam(adapt(x), state, y),
1268                            adapt(x) == None)
1269                        for (x, y) in self.property.local_remote_pairs])
1270
1271            criterion = sql.and_(*[
1272                x == y for (x, y) in
1273                zip(
1274                    self.property.mapper.primary_key,
1275                    self.property.mapper.primary_key_from_instance(other)
1276                )
1277            ])
1278
1279            return ~self._criterion_exists(criterion)
1280
1281        def __ne__(self, other):
1282            """Implement the ``!=`` operator.
1283
1284            In a many-to-one context, such as::
1285
1286              MyClass.some_prop != <some object>
1287
1288            This will typically produce a clause such as::
1289
1290              mytable.related_id != <some id>
1291
1292            Where ``<some id>`` is the primary key of the
1293            given object.
1294
1295            The ``!=`` operator provides partial functionality for non-
1296            many-to-one comparisons:
1297
1298            * Comparisons against collections are not supported.
1299              Use
1300              :meth:`~.RelationshipProperty.Comparator.contains`
1301              in conjunction with :func:`~.expression.not_`.
1302            * Compared to a scalar one-to-many, will produce a
1303              clause that compares the target columns in the parent to
1304              the given target.
1305            * Compared to a scalar many-to-many, an alias
1306              of the association table will be rendered as
1307              well, forming a natural join that is part of the
1308              main body of the query. This will not work for
1309              queries that go beyond simple AND conjunctions of
1310              comparisons, such as those which use OR. Use
1311              explicit joins, outerjoins, or
1312              :meth:`~.RelationshipProperty.Comparator.has` in
1313              conjunction with :func:`~.expression.not_` for
1314              more comprehensive non-many-to-one scalar
1315              membership tests.
1316            * Comparisons against ``None`` given in a one-to-many
1317              or many-to-many context produce an EXISTS clause.
1318
1319            """
1320            if isinstance(other, (util.NoneType, expression.Null)):
1321                if self.property.direction == MANYTOONE:
1322                    return _orm_annotate(~self.property._optimized_compare(
1323                        None, adapt_source=self.adapter))
1324
1325                else:
1326                    return self._criterion_exists()
1327            elif self.property.uselist:
1328                raise sa_exc.InvalidRequestError(
1329                    "Can't compare a collection"
1330                    " to an object or collection; use "
1331                    "contains() to test for membership.")
1332            else:
1333                return _orm_annotate(self.__negated_contains_or_equals(other))
1334
1335        @util.memoized_property
1336        def property(self):
1337            if mapperlib.Mapper._new_mappers:
1338                mapperlib.Mapper._configure_all()
1339            return self.prop
1340
1341    def _with_parent(self, instance, alias_secondary=True):
1342        assert instance is not None
1343        return self._optimized_compare(
1344            instance, value_is_parent=True, alias_secondary=alias_secondary)
1345
1346    def _optimized_compare(self, state, value_is_parent=False,
1347                           adapt_source=None,
1348                           alias_secondary=True):
1349        if state is not None:
1350            state = attributes.instance_state(state)
1351
1352        reverse_direction = not value_is_parent
1353
1354        if state is None:
1355            return self._lazy_none_clause(
1356                reverse_direction,
1357                adapt_source=adapt_source)
1358
1359        if not reverse_direction:
1360            criterion, bind_to_col = \
1361                self._lazy_strategy._lazywhere, \
1362                self._lazy_strategy._bind_to_col
1363        else:
1364            criterion, bind_to_col = \
1365                self._lazy_strategy._rev_lazywhere, \
1366                self._lazy_strategy._rev_bind_to_col
1367
1368        if reverse_direction:
1369            mapper = self.mapper
1370        else:
1371            mapper = self.parent
1372
1373        dict_ = attributes.instance_dict(state.obj())
1374
1375        def visit_bindparam(bindparam):
1376            if bindparam._identifying_key in bind_to_col:
1377                bindparam.callable = self._get_attr_w_warn_on_none(
1378                    bind_to_col[bindparam._identifying_key],
1379                    mapper._get_state_attr_by_column,
1380                    state, dict_,
1381                    bind_to_col[bindparam._identifying_key],
1382                    passive=attributes.PASSIVE_OFF)
1383
1384        if self.secondary is not None and alias_secondary:
1385            criterion = ClauseAdapter(
1386                self.secondary.alias()).\
1387                traverse(criterion)
1388
1389        criterion = visitors.cloned_traverse(
1390            criterion, {}, {'bindparam': visit_bindparam})
1391
1392        if adapt_source:
1393            criterion = adapt_source(criterion)
1394        return criterion
1395
1396    def _get_attr_w_warn_on_none(self, column, fn, *arg, **kw):
1397        def _go():
1398            value = fn(*arg, **kw)
1399            if value is None:
1400                util.warn(
1401                    "Got None for value of column %s; this is unsupported "
1402                    "for a relationship comparison and will not "
1403                    "currently produce an IS comparison "
1404                    "(but may in a future release)" % column)
1405            return value
1406        return _go
1407
1408    def _lazy_none_clause(self, reverse_direction=False, adapt_source=None):
1409        if not reverse_direction:
1410            criterion, bind_to_col = \
1411                self._lazy_strategy._lazywhere, \
1412                self._lazy_strategy._bind_to_col
1413        else:
1414            criterion, bind_to_col = \
1415                self._lazy_strategy._rev_lazywhere, \
1416                self._lazy_strategy._rev_bind_to_col
1417
1418        criterion = adapt_criterion_to_null(criterion, bind_to_col)
1419
1420        if adapt_source:
1421            criterion = adapt_source(criterion)
1422        return criterion
1423
1424    def __str__(self):
1425        return str(self.parent.class_.__name__) + "." + self.key
1426
1427    def merge(self,
1428              session,
1429              source_state,
1430              source_dict,
1431              dest_state,
1432              dest_dict,
1433              load, _recursive):
1434
1435        if load:
1436            for r in self._reverse_property:
1437                if (source_state, r) in _recursive:
1438                    return
1439
1440        if "merge" not in self._cascade:
1441            return
1442
1443        if self.key not in source_dict:
1444            return
1445
1446        if self.uselist:
1447            instances = source_state.get_impl(self.key).\
1448                get(source_state, source_dict)
1449            if hasattr(instances, '_sa_adapter'):
1450                # convert collections to adapters to get a true iterator
1451                instances = instances._sa_adapter
1452
1453            if load:
1454                # for a full merge, pre-load the destination collection,
1455                # so that individual _merge of each item pulls from identity
1456                # map for those already present.
1457                # also assumes CollectionAttrbiuteImpl behavior of loading
1458                # "old" list in any case
1459                dest_state.get_impl(self.key).get(dest_state, dest_dict)
1460
1461            dest_list = []
1462            for current in instances:
1463                current_state = attributes.instance_state(current)
1464                current_dict = attributes.instance_dict(current)
1465                _recursive[(current_state, self)] = True
1466                obj = session._merge(current_state, current_dict,
1467                                     load=load, _recursive=_recursive)
1468                if obj is not None:
1469                    dest_list.append(obj)
1470
1471            if not load:
1472                coll = attributes.init_state_collection(dest_state,
1473                                                        dest_dict, self.key)
1474                for c in dest_list:
1475                    coll.append_without_event(c)
1476            else:
1477                dest_state.get_impl(self.key)._set_iterable(
1478                    dest_state, dest_dict, dest_list)
1479        else:
1480            current = source_dict[self.key]
1481            if current is not None:
1482                current_state = attributes.instance_state(current)
1483                current_dict = attributes.instance_dict(current)
1484                _recursive[(current_state, self)] = True
1485                obj = session._merge(current_state, current_dict,
1486                                     load=load, _recursive=_recursive)
1487            else:
1488                obj = None
1489
1490            if not load:
1491                dest_dict[self.key] = obj
1492            else:
1493                dest_state.get_impl(self.key).set(dest_state,
1494                                                  dest_dict, obj, None)
1495
1496    def _value_as_iterable(self, state, dict_, key,
1497                           passive=attributes.PASSIVE_OFF):
1498        """Return a list of tuples (state, obj) for the given
1499        key.
1500
1501        returns an empty list if the value is None/empty/PASSIVE_NO_RESULT
1502        """
1503
1504        impl = state.manager[key].impl
1505        x = impl.get(state, dict_, passive=passive)
1506        if x is attributes.PASSIVE_NO_RESULT or x is None:
1507            return []
1508        elif hasattr(impl, 'get_collection'):
1509            return [
1510                (attributes.instance_state(o), o) for o in
1511                impl.get_collection(state, dict_, x, passive=passive)
1512            ]
1513        else:
1514            return [(attributes.instance_state(x), x)]
1515
1516    def cascade_iterator(self, type_, state, dict_,
1517                         visited_states, halt_on=None):
1518        # assert type_ in self._cascade
1519
1520        # only actively lazy load on the 'delete' cascade
1521        if type_ != 'delete' or self.passive_deletes:
1522            passive = attributes.PASSIVE_NO_INITIALIZE
1523        else:
1524            passive = attributes.PASSIVE_OFF
1525
1526        if type_ == 'save-update':
1527            tuples = state.manager[self.key].impl.\
1528                get_all_pending(state, dict_)
1529
1530        else:
1531            tuples = self._value_as_iterable(state, dict_, self.key,
1532                                             passive=passive)
1533
1534        skip_pending = type_ == 'refresh-expire' and 'delete-orphan' \
1535            not in self._cascade
1536
1537        for instance_state, c in tuples:
1538            if instance_state in visited_states:
1539                continue
1540
1541            if c is None:
1542                # would like to emit a warning here, but
1543                # would not be consistent with collection.append(None)
1544                # current behavior of silently skipping.
1545                # see [ticket:2229]
1546                continue
1547
1548            instance_dict = attributes.instance_dict(c)
1549
1550            if halt_on and halt_on(instance_state):
1551                continue
1552
1553            if skip_pending and not instance_state.key:
1554                continue
1555
1556            instance_mapper = instance_state.manager.mapper
1557
1558            if not instance_mapper.isa(self.mapper.class_manager.mapper):
1559                raise AssertionError("Attribute '%s' on class '%s' "
1560                                     "doesn't handle objects "
1561                                     "of type '%s'" % (
1562                                         self.key,
1563                                         self.parent.class_,
1564                                         c.__class__
1565                                     ))
1566
1567            visited_states.add(instance_state)
1568
1569            yield c, instance_mapper, instance_state, instance_dict
1570
1571    def _add_reverse_property(self, key):
1572        other = self.mapper.get_property(key, _configure_mappers=False)
1573        self._reverse_property.add(other)
1574        other._reverse_property.add(self)
1575
1576        if not other.mapper.common_parent(self.parent):
1577            raise sa_exc.ArgumentError(
1578                'reverse_property %r on '
1579                'relationship %s references relationship %s, which '
1580                'does not reference mapper %s' %
1581                (key, self, other, self.parent))
1582
1583        if self.direction in (ONETOMANY, MANYTOONE) and self.direction \
1584                == other.direction:
1585            raise sa_exc.ArgumentError(
1586                '%s and back-reference %s are '
1587                'both of the same direction %r.  Did you mean to '
1588                'set remote_side on the many-to-one side ?' %
1589                (other, self, self.direction))
1590
1591    @util.memoized_property
1592    def mapper(self):
1593        """Return the targeted :class:`.Mapper` for this
1594        :class:`.RelationshipProperty`.
1595
1596        This is a lazy-initializing static attribute.
1597
1598        """
1599        if util.callable(self.argument) and \
1600                not isinstance(self.argument, (type, mapperlib.Mapper)):
1601            argument = self.argument()
1602        else:
1603            argument = self.argument
1604
1605        if isinstance(argument, type):
1606            mapper_ = mapperlib.class_mapper(argument,
1607                                             configure=False)
1608        elif isinstance(self.argument, mapperlib.Mapper):
1609            mapper_ = argument
1610        else:
1611            raise sa_exc.ArgumentError(
1612                "relationship '%s' expects "
1613                "a class or a mapper argument (received: %s)"
1614                % (self.key, type(argument)))
1615        return mapper_
1616
1617    @util.memoized_property
1618    @util.deprecated("0.7", "Use .target")
1619    def table(self):
1620        """Return the selectable linked to this
1621        :class:`.RelationshipProperty` object's target
1622        :class:`.Mapper`.
1623        """
1624        return self.target
1625
1626    def do_init(self):
1627        self._check_conflicts()
1628        self._process_dependent_arguments()
1629        self._setup_join_conditions()
1630        self._check_cascade_settings(self._cascade)
1631        self._post_init()
1632        self._generate_backref()
1633        self._join_condition._warn_for_conflicting_sync_targets()
1634        super(RelationshipProperty, self).do_init()
1635        self._lazy_strategy = self._get_strategy((("lazy", "select"),))
1636
1637    def _process_dependent_arguments(self):
1638        """Convert incoming configuration arguments to their
1639        proper form.
1640
1641        Callables are resolved, ORM annotations removed.
1642
1643        """
1644        # accept callables for other attributes which may require
1645        # deferred initialization.  This technique is used
1646        # by declarative "string configs" and some recipes.
1647        for attr in (
1648            'order_by', 'primaryjoin', 'secondaryjoin',
1649            'secondary', '_user_defined_foreign_keys', 'remote_side',
1650        ):
1651            attr_value = getattr(self, attr)
1652            if util.callable(attr_value):
1653                setattr(self, attr, attr_value())
1654
1655        # remove "annotations" which are present if mapped class
1656        # descriptors are used to create the join expression.
1657        for attr in 'primaryjoin', 'secondaryjoin':
1658            val = getattr(self, attr)
1659            if val is not None:
1660                setattr(self, attr, _orm_deannotate(
1661                    expression._only_column_elements(val, attr))
1662                )
1663
1664        # ensure expressions in self.order_by, foreign_keys,
1665        # remote_side are all columns, not strings.
1666        if self.order_by is not False and self.order_by is not None:
1667            self.order_by = [
1668                expression._only_column_elements(x, "order_by")
1669                for x in
1670                util.to_list(self.order_by)]
1671
1672        self._user_defined_foreign_keys = \
1673            util.column_set(
1674                expression._only_column_elements(x, "foreign_keys")
1675                for x in util.to_column_set(
1676                    self._user_defined_foreign_keys
1677                ))
1678
1679        self.remote_side = \
1680            util.column_set(
1681                expression._only_column_elements(x, "remote_side")
1682                for x in
1683                util.to_column_set(self.remote_side))
1684
1685        self.target = self.mapper.mapped_table
1686
1687    def _setup_join_conditions(self):
1688        self._join_condition = jc = JoinCondition(
1689            parent_selectable=self.parent.mapped_table,
1690            child_selectable=self.mapper.mapped_table,
1691            parent_local_selectable=self.parent.local_table,
1692            child_local_selectable=self.mapper.local_table,
1693            primaryjoin=self.primaryjoin,
1694            secondary=self.secondary,
1695            secondaryjoin=self.secondaryjoin,
1696            parent_equivalents=self.parent._equivalent_columns,
1697            child_equivalents=self.mapper._equivalent_columns,
1698            consider_as_foreign_keys=self._user_defined_foreign_keys,
1699            local_remote_pairs=self.local_remote_pairs,
1700            remote_side=self.remote_side,
1701            self_referential=self._is_self_referential,
1702            prop=self,
1703            support_sync=not self.viewonly,
1704            can_be_synced_fn=self._columns_are_mapped
1705        )
1706        self.primaryjoin = jc.deannotated_primaryjoin
1707        self.secondaryjoin = jc.deannotated_secondaryjoin
1708        self.direction = jc.direction
1709        self.local_remote_pairs = jc.local_remote_pairs
1710        self.remote_side = jc.remote_columns
1711        self.local_columns = jc.local_columns
1712        self.synchronize_pairs = jc.synchronize_pairs
1713        self._calculated_foreign_keys = jc.foreign_key_columns
1714        self.secondary_synchronize_pairs = jc.secondary_synchronize_pairs
1715
1716    def _check_conflicts(self):
1717        """Test that this relationship is legal, warn about
1718        inheritance conflicts."""
1719
1720        if self.parent.non_primary and not mapperlib.class_mapper(
1721                self.parent.class_,
1722                configure=False).has_property(self.key):
1723            raise sa_exc.ArgumentError(
1724                "Attempting to assign a new "
1725                "relationship '%s' to a non-primary mapper on "
1726                "class '%s'.  New relationships can only be added "
1727                "to the primary mapper, i.e. the very first mapper "
1728                "created for class '%s' " %
1729                (self.key, self.parent.class_.__name__,
1730                 self.parent.class_.__name__))
1731
1732        # check for conflicting relationship() on superclass
1733        if not self.parent.concrete:
1734            for inheriting in self.parent.iterate_to_root():
1735                if inheriting is not self.parent \
1736                        and inheriting.has_property(self.key):
1737                    util.warn("Warning: relationship '%s' on mapper "
1738                              "'%s' supersedes the same relationship "
1739                              "on inherited mapper '%s'; this can "
1740                              "cause dependency issues during flush"
1741                              % (self.key, self.parent, inheriting))
1742
1743    def _get_cascade(self):
1744        """Return the current cascade setting for this
1745        :class:`.RelationshipProperty`.
1746        """
1747        return self._cascade
1748
1749    def _set_cascade(self, cascade):
1750        cascade = CascadeOptions(cascade)
1751        if 'mapper' in self.__dict__:
1752            self._check_cascade_settings(cascade)
1753        self._cascade = cascade
1754
1755        if self._dependency_processor:
1756            self._dependency_processor.cascade = cascade
1757
1758    cascade = property(_get_cascade, _set_cascade)
1759
1760    def _check_cascade_settings(self, cascade):
1761        if cascade.delete_orphan and not self.single_parent \
1762            and (self.direction is MANYTOMANY or self.direction
1763                 is MANYTOONE):
1764            raise sa_exc.ArgumentError(
1765                'On %s, delete-orphan cascade is not supported '
1766                'on a many-to-many or many-to-one relationship '
1767                'when single_parent is not set.   Set '
1768                'single_parent=True on the relationship().'
1769                % self)
1770        if self.direction is MANYTOONE and self.passive_deletes:
1771            util.warn("On %s, 'passive_deletes' is normally configured "
1772                      "on one-to-many, one-to-one, many-to-many "
1773                      "relationships only."
1774                      % self)
1775
1776        if self.passive_deletes == 'all' and \
1777            ("delete" in cascade or
1778             "delete-orphan" in cascade):
1779            raise sa_exc.ArgumentError(
1780                "On %s, can't set passive_deletes='all' in conjunction "
1781                "with 'delete' or 'delete-orphan' cascade" % self)
1782
1783        if cascade.delete_orphan:
1784            self.mapper.primary_mapper()._delete_orphans.append(
1785                (self.key, self.parent.class_)
1786            )
1787
1788    def _columns_are_mapped(self, *cols):
1789        """Return True if all columns in the given collection are
1790        mapped by the tables referenced by this :class:`.Relationship`.
1791
1792        """
1793        for c in cols:
1794            if self.secondary is not None \
1795                    and self.secondary.c.contains_column(c):
1796                continue
1797            if not self.parent.mapped_table.c.contains_column(c) and \
1798                    not self.target.c.contains_column(c):
1799                return False
1800        return True
1801
1802    def _generate_backref(self):
1803        """Interpret the 'backref' instruction to create a
1804        :func:`.relationship` complementary to this one."""
1805
1806        if self.parent.non_primary:
1807            return
1808        if self.backref is not None and not self.back_populates:
1809            if isinstance(self.backref, util.string_types):
1810                backref_key, kwargs = self.backref, {}
1811            else:
1812                backref_key, kwargs = self.backref
1813            mapper = self.mapper.primary_mapper()
1814
1815            check = set(mapper.iterate_to_root()).\
1816                union(mapper.self_and_descendants)
1817            for m in check:
1818                if m.has_property(backref_key):
1819                    raise sa_exc.ArgumentError(
1820                        "Error creating backref "
1821                        "'%s' on relationship '%s': property of that "
1822                        "name exists on mapper '%s'" %
1823                        (backref_key, self, m))
1824
1825            # determine primaryjoin/secondaryjoin for the
1826            # backref.  Use the one we had, so that
1827            # a custom join doesn't have to be specified in
1828            # both directions.
1829            if self.secondary is not None:
1830                # for many to many, just switch primaryjoin/
1831                # secondaryjoin.   use the annotated
1832                # pj/sj on the _join_condition.
1833                pj = kwargs.pop(
1834                    'primaryjoin',
1835                    self._join_condition.secondaryjoin_minus_local)
1836                sj = kwargs.pop(
1837                    'secondaryjoin',
1838                    self._join_condition.primaryjoin_minus_local)
1839            else:
1840                pj = kwargs.pop(
1841                    'primaryjoin',
1842                    self._join_condition.primaryjoin_reverse_remote)
1843                sj = kwargs.pop('secondaryjoin', None)
1844                if sj:
1845                    raise sa_exc.InvalidRequestError(
1846                        "Can't assign 'secondaryjoin' on a backref "
1847                        "against a non-secondary relationship."
1848                    )
1849
1850            foreign_keys = kwargs.pop('foreign_keys',
1851                                      self._user_defined_foreign_keys)
1852            parent = self.parent.primary_mapper()
1853            kwargs.setdefault('viewonly', self.viewonly)
1854            kwargs.setdefault('post_update', self.post_update)
1855            kwargs.setdefault('passive_updates', self.passive_updates)
1856            self.back_populates = backref_key
1857            relationship = RelationshipProperty(
1858                parent, self.secondary,
1859                pj, sj,
1860                foreign_keys=foreign_keys,
1861                back_populates=self.key,
1862                **kwargs)
1863            mapper._configure_property(backref_key, relationship)
1864
1865        if self.back_populates:
1866            self._add_reverse_property(self.back_populates)
1867
1868    def _post_init(self):
1869        if self.uselist is None:
1870            self.uselist = self.direction is not MANYTOONE
1871        if not self.viewonly:
1872            self._dependency_processor = \
1873                dependency.DependencyProcessor.from_relationship(self)
1874
1875    @util.memoized_property
1876    def _use_get(self):
1877        """memoize the 'use_get' attribute of this RelationshipLoader's
1878        lazyloader."""
1879
1880        strategy = self._lazy_strategy
1881        return strategy.use_get
1882
1883    @util.memoized_property
1884    def _is_self_referential(self):
1885        return self.mapper.common_parent(self.parent)
1886
1887    def _create_joins(self, source_polymorphic=False,
1888                      source_selectable=None, dest_polymorphic=False,
1889                      dest_selectable=None, of_type=None):
1890        if source_selectable is None:
1891            if source_polymorphic and self.parent.with_polymorphic:
1892                source_selectable = self.parent._with_polymorphic_selectable
1893
1894        aliased = False
1895        if dest_selectable is None:
1896            if dest_polymorphic and self.mapper.with_polymorphic:
1897                dest_selectable = self.mapper._with_polymorphic_selectable
1898                aliased = True
1899            else:
1900                dest_selectable = self.mapper.mapped_table
1901
1902            if self._is_self_referential and source_selectable is None:
1903                dest_selectable = dest_selectable.alias()
1904                aliased = True
1905        else:
1906            aliased = True
1907
1908        dest_mapper = of_type or self.mapper
1909
1910        single_crit = dest_mapper._single_table_criterion
1911        aliased = aliased or (source_selectable is not None)
1912
1913        primaryjoin, secondaryjoin, secondary, target_adapter, dest_selectable = \
1914            self._join_condition.join_targets(
1915                source_selectable, dest_selectable, aliased, single_crit
1916            )
1917        if source_selectable is None:
1918            source_selectable = self.parent.local_table
1919        if dest_selectable is None:
1920            dest_selectable = self.mapper.local_table
1921        return (primaryjoin, secondaryjoin, source_selectable,
1922                dest_selectable, secondary, target_adapter)
1923
1924
1925def _annotate_columns(element, annotations):
1926    def clone(elem):
1927        if isinstance(elem, expression.ColumnClause):
1928            elem = elem._annotate(annotations.copy())
1929        elem._copy_internals(clone=clone)
1930        return elem
1931
1932    if element is not None:
1933        element = clone(element)
1934    return element
1935
1936
1937class JoinCondition(object):
1938    def __init__(self,
1939                 parent_selectable,
1940                 child_selectable,
1941                 parent_local_selectable,
1942                 child_local_selectable,
1943                 primaryjoin=None,
1944                 secondary=None,
1945                 secondaryjoin=None,
1946                 parent_equivalents=None,
1947                 child_equivalents=None,
1948                 consider_as_foreign_keys=None,
1949                 local_remote_pairs=None,
1950                 remote_side=None,
1951                 self_referential=False,
1952                 prop=None,
1953                 support_sync=True,
1954                 can_be_synced_fn=lambda *c: True
1955                 ):
1956        self.parent_selectable = parent_selectable
1957        self.parent_local_selectable = parent_local_selectable
1958        self.child_selectable = child_selectable
1959        self.child_local_selectable = child_local_selectable
1960        self.parent_equivalents = parent_equivalents
1961        self.child_equivalents = child_equivalents
1962        self.primaryjoin = primaryjoin
1963        self.secondaryjoin = secondaryjoin
1964        self.secondary = secondary
1965        self.consider_as_foreign_keys = consider_as_foreign_keys
1966        self._local_remote_pairs = local_remote_pairs
1967        self._remote_side = remote_side
1968        self.prop = prop
1969        self.self_referential = self_referential
1970        self.support_sync = support_sync
1971        self.can_be_synced_fn = can_be_synced_fn
1972        self._determine_joins()
1973        self._annotate_fks()
1974        self._annotate_remote()
1975        self._annotate_local()
1976        self._setup_pairs()
1977        self._check_foreign_cols(self.primaryjoin, True)
1978        if self.secondaryjoin is not None:
1979            self._check_foreign_cols(self.secondaryjoin, False)
1980        self._determine_direction()
1981        self._check_remote_side()
1982        self._log_joins()
1983
1984    def _log_joins(self):
1985        if self.prop is None:
1986            return
1987        log = self.prop.logger
1988        log.info('%s setup primary join %s', self.prop,
1989                 self.primaryjoin)
1990        log.info('%s setup secondary join %s', self.prop,
1991                 self.secondaryjoin)
1992        log.info('%s synchronize pairs [%s]', self.prop,
1993                 ','.join('(%s => %s)' % (l, r) for (l, r) in
1994                          self.synchronize_pairs))
1995        log.info('%s secondary synchronize pairs [%s]', self.prop,
1996                 ','.join('(%s => %s)' % (l, r) for (l, r) in
1997                          self.secondary_synchronize_pairs or []))
1998        log.info('%s local/remote pairs [%s]', self.prop,
1999                 ','.join('(%s / %s)' % (l, r) for (l, r) in
2000                          self.local_remote_pairs))
2001        log.info('%s remote columns [%s]', self.prop,
2002                 ','.join('%s' % col for col in self.remote_columns)
2003                 )
2004        log.info('%s local columns [%s]', self.prop,
2005                 ','.join('%s' % col for col in self.local_columns)
2006                 )
2007        log.info('%s relationship direction %s', self.prop,
2008                 self.direction)
2009
2010    def _determine_joins(self):
2011        """Determine the 'primaryjoin' and 'secondaryjoin' attributes,
2012        if not passed to the constructor already.
2013
2014        This is based on analysis of the foreign key relationships
2015        between the parent and target mapped selectables.
2016
2017        """
2018        if self.secondaryjoin is not None and self.secondary is None:
2019            raise sa_exc.ArgumentError(
2020                "Property %s specified with secondary "
2021                "join condition but "
2022                "no secondary argument" % self.prop)
2023
2024        # find a join between the given mapper's mapped table and
2025        # the given table. will try the mapper's local table first
2026        # for more specificity, then if not found will try the more
2027        # general mapped table, which in the case of inheritance is
2028        # a join.
2029        try:
2030            consider_as_foreign_keys = self.consider_as_foreign_keys or None
2031            if self.secondary is not None:
2032                if self.secondaryjoin is None:
2033                    self.secondaryjoin = \
2034                        join_condition(
2035                            self.child_selectable,
2036                            self.secondary,
2037                            a_subset=self.child_local_selectable,
2038                            consider_as_foreign_keys=consider_as_foreign_keys
2039                        )
2040                if self.primaryjoin is None:
2041                    self.primaryjoin = \
2042                        join_condition(
2043                            self.parent_selectable,
2044                            self.secondary,
2045                            a_subset=self.parent_local_selectable,
2046                            consider_as_foreign_keys=consider_as_foreign_keys
2047                        )
2048            else:
2049                if self.primaryjoin is None:
2050                    self.primaryjoin = \
2051                        join_condition(
2052                            self.parent_selectable,
2053                            self.child_selectable,
2054                            a_subset=self.parent_local_selectable,
2055                            consider_as_foreign_keys=consider_as_foreign_keys
2056                        )
2057        except sa_exc.NoForeignKeysError:
2058            if self.secondary is not None:
2059                raise sa_exc.NoForeignKeysError(
2060                    "Could not determine join "
2061                    "condition between parent/child tables on "
2062                    "relationship %s - there are no foreign keys "
2063                    "linking these tables via secondary table '%s'.  "
2064                    "Ensure that referencing columns are associated "
2065                    "with a ForeignKey or ForeignKeyConstraint, or "
2066                    "specify 'primaryjoin' and 'secondaryjoin' "
2067                    "expressions." % (self.prop, self.secondary))
2068            else:
2069                raise sa_exc.NoForeignKeysError(
2070                    "Could not determine join "
2071                    "condition between parent/child tables on "
2072                    "relationship %s - there are no foreign keys "
2073                    "linking these tables.  "
2074                    "Ensure that referencing columns are associated "
2075                    "with a ForeignKey or ForeignKeyConstraint, or "
2076                    "specify a 'primaryjoin' expression." % self.prop)
2077        except sa_exc.AmbiguousForeignKeysError:
2078            if self.secondary is not None:
2079                raise sa_exc.AmbiguousForeignKeysError(
2080                    "Could not determine join "
2081                    "condition between parent/child tables on "
2082                    "relationship %s - there are multiple foreign key "
2083                    "paths linking the tables via secondary table '%s'.  "
2084                    "Specify the 'foreign_keys' "
2085                    "argument, providing a list of those columns which "
2086                    "should be counted as containing a foreign key "
2087                    "reference from the secondary table to each of the "
2088                    "parent and child tables."
2089                    % (self.prop, self.secondary))
2090            else:
2091                raise sa_exc.AmbiguousForeignKeysError(
2092                    "Could not determine join "
2093                    "condition between parent/child tables on "
2094                    "relationship %s - there are multiple foreign key "
2095                    "paths linking the tables.  Specify the "
2096                    "'foreign_keys' argument, providing a list of those "
2097                    "columns which should be counted as containing a "
2098                    "foreign key reference to the parent table."
2099                    % self.prop)
2100
2101    @property
2102    def primaryjoin_minus_local(self):
2103        return _deep_deannotate(self.primaryjoin, values=("local", "remote"))
2104
2105    @property
2106    def secondaryjoin_minus_local(self):
2107        return _deep_deannotate(self.secondaryjoin,
2108                                values=("local", "remote"))
2109
2110    @util.memoized_property
2111    def primaryjoin_reverse_remote(self):
2112        """Return the primaryjoin condition suitable for the
2113        "reverse" direction.
2114
2115        If the primaryjoin was delivered here with pre-existing
2116        "remote" annotations, the local/remote annotations
2117        are reversed.  Otherwise, the local/remote annotations
2118        are removed.
2119
2120        """
2121        if self._has_remote_annotations:
2122            def replace(element):
2123                if "remote" in element._annotations:
2124                    v = element._annotations.copy()
2125                    del v['remote']
2126                    v['local'] = True
2127                    return element._with_annotations(v)
2128                elif "local" in element._annotations:
2129                    v = element._annotations.copy()
2130                    del v['local']
2131                    v['remote'] = True
2132                    return element._with_annotations(v)
2133            return visitors.replacement_traverse(
2134                self.primaryjoin, {}, replace)
2135        else:
2136            if self._has_foreign_annotations:
2137                # TODO: coverage
2138                return _deep_deannotate(self.primaryjoin,
2139                                        values=("local", "remote"))
2140            else:
2141                return _deep_deannotate(self.primaryjoin)
2142
2143    def _has_annotation(self, clause, annotation):
2144        for col in visitors.iterate(clause, {}):
2145            if annotation in col._annotations:
2146                return True
2147        else:
2148            return False
2149
2150    @util.memoized_property
2151    def _has_foreign_annotations(self):
2152        return self._has_annotation(self.primaryjoin, "foreign")
2153
2154    @util.memoized_property
2155    def _has_remote_annotations(self):
2156        return self._has_annotation(self.primaryjoin, "remote")
2157
2158    def _annotate_fks(self):
2159        """Annotate the primaryjoin and secondaryjoin
2160        structures with 'foreign' annotations marking columns
2161        considered as foreign.
2162
2163        """
2164        if self._has_foreign_annotations:
2165            return
2166
2167        if self.consider_as_foreign_keys:
2168            self._annotate_from_fk_list()
2169        else:
2170            self._annotate_present_fks()
2171
2172    def _annotate_from_fk_list(self):
2173        def check_fk(col):
2174            if col in self.consider_as_foreign_keys:
2175                return col._annotate({"foreign": True})
2176        self.primaryjoin = visitors.replacement_traverse(
2177            self.primaryjoin,
2178            {},
2179            check_fk
2180        )
2181        if self.secondaryjoin is not None:
2182            self.secondaryjoin = visitors.replacement_traverse(
2183                self.secondaryjoin,
2184                {},
2185                check_fk
2186            )
2187
2188    def _annotate_present_fks(self):
2189        if self.secondary is not None:
2190            secondarycols = util.column_set(self.secondary.c)
2191        else:
2192            secondarycols = set()
2193
2194        def is_foreign(a, b):
2195            if isinstance(a, schema.Column) and \
2196                    isinstance(b, schema.Column):
2197                if a.references(b):
2198                    return a
2199                elif b.references(a):
2200                    return b
2201
2202            if secondarycols:
2203                if a in secondarycols and b not in secondarycols:
2204                    return a
2205                elif b in secondarycols and a not in secondarycols:
2206                    return b
2207
2208        def visit_binary(binary):
2209            if not isinstance(binary.left, sql.ColumnElement) or \
2210                    not isinstance(binary.right, sql.ColumnElement):
2211                return
2212
2213            if "foreign" not in binary.left._annotations and \
2214                    "foreign" not in binary.right._annotations:
2215                col = is_foreign(binary.left, binary.right)
2216                if col is not None:
2217                    if col.compare(binary.left):
2218                        binary.left = binary.left._annotate(
2219                            {"foreign": True})
2220                    elif col.compare(binary.right):
2221                        binary.right = binary.right._annotate(
2222                            {"foreign": True})
2223
2224        self.primaryjoin = visitors.cloned_traverse(
2225            self.primaryjoin,
2226            {},
2227            {"binary": visit_binary}
2228        )
2229        if self.secondaryjoin is not None:
2230            self.secondaryjoin = visitors.cloned_traverse(
2231                self.secondaryjoin,
2232                {},
2233                {"binary": visit_binary}
2234            )
2235
2236    def _refers_to_parent_table(self):
2237        """Return True if the join condition contains column
2238        comparisons where both columns are in both tables.
2239
2240        """
2241        pt = self.parent_selectable
2242        mt = self.child_selectable
2243        result = [False]
2244
2245        def visit_binary(binary):
2246            c, f = binary.left, binary.right
2247            if (
2248                isinstance(c, expression.ColumnClause) and
2249                isinstance(f, expression.ColumnClause) and
2250                pt.is_derived_from(c.table) and
2251                pt.is_derived_from(f.table) and
2252                mt.is_derived_from(c.table) and
2253                mt.is_derived_from(f.table)
2254            ):
2255                result[0] = True
2256        visitors.traverse(
2257            self.primaryjoin,
2258            {},
2259            {"binary": visit_binary}
2260        )
2261        return result[0]
2262
2263    def _tables_overlap(self):
2264        """Return True if parent/child tables have some overlap."""
2265
2266        return selectables_overlap(
2267            self.parent_selectable, self.child_selectable)
2268
2269    def _annotate_remote(self):
2270        """Annotate the primaryjoin and secondaryjoin
2271        structures with 'remote' annotations marking columns
2272        considered as part of the 'remote' side.
2273
2274        """
2275        if self._has_remote_annotations:
2276            return
2277
2278        if self.secondary is not None:
2279            self._annotate_remote_secondary()
2280        elif self._local_remote_pairs or self._remote_side:
2281            self._annotate_remote_from_args()
2282        elif self._refers_to_parent_table():
2283            self._annotate_selfref(lambda col: "foreign" in col._annotations, False)
2284        elif self._tables_overlap():
2285            self._annotate_remote_with_overlap()
2286        else:
2287            self._annotate_remote_distinct_selectables()
2288
2289    def _annotate_remote_secondary(self):
2290        """annotate 'remote' in primaryjoin, secondaryjoin
2291        when 'secondary' is present.
2292
2293        """
2294        def repl(element):
2295            if self.secondary.c.contains_column(element):
2296                return element._annotate({"remote": True})
2297        self.primaryjoin = visitors.replacement_traverse(
2298            self.primaryjoin, {}, repl)
2299        self.secondaryjoin = visitors.replacement_traverse(
2300            self.secondaryjoin, {}, repl)
2301
2302    def _annotate_selfref(self, fn, remote_side_given):
2303        """annotate 'remote' in primaryjoin, secondaryjoin
2304        when the relationship is detected as self-referential.
2305
2306        """
2307        def visit_binary(binary):
2308            equated = binary.left.compare(binary.right)
2309            if isinstance(binary.left, expression.ColumnClause) and \
2310                    isinstance(binary.right, expression.ColumnClause):
2311                # assume one to many - FKs are "remote"
2312                if fn(binary.left):
2313                    binary.left = binary.left._annotate({"remote": True})
2314                if fn(binary.right) and not equated:
2315                    binary.right = binary.right._annotate(
2316                        {"remote": True})
2317            elif not remote_side_given:
2318                self._warn_non_column_elements()
2319
2320        self.primaryjoin = visitors.cloned_traverse(
2321            self.primaryjoin, {},
2322            {"binary": visit_binary})
2323
2324    def _annotate_remote_from_args(self):
2325        """annotate 'remote' in primaryjoin, secondaryjoin
2326        when the 'remote_side' or '_local_remote_pairs'
2327        arguments are used.
2328
2329        """
2330        if self._local_remote_pairs:
2331            if self._remote_side:
2332                raise sa_exc.ArgumentError(
2333                    "remote_side argument is redundant "
2334                    "against more detailed _local_remote_side "
2335                    "argument.")
2336
2337            remote_side = [r for (l, r) in self._local_remote_pairs]
2338        else:
2339            remote_side = self._remote_side
2340
2341        if self._refers_to_parent_table():
2342            self._annotate_selfref(lambda col: col in remote_side, True)
2343        else:
2344            def repl(element):
2345                if element in remote_side:
2346                    return element._annotate({"remote": True})
2347            self.primaryjoin = visitors.replacement_traverse(
2348                self.primaryjoin, {}, repl)
2349
2350    def _annotate_remote_with_overlap(self):
2351        """annotate 'remote' in primaryjoin, secondaryjoin
2352        when the parent/child tables have some set of
2353        tables in common, though is not a fully self-referential
2354        relationship.
2355
2356        """
2357        def visit_binary(binary):
2358            binary.left, binary.right = proc_left_right(binary.left,
2359                                                        binary.right)
2360            binary.right, binary.left = proc_left_right(binary.right,
2361                                                        binary.left)
2362
2363        check_entities = self.prop is not None and \
2364            self.prop.mapper is not self.prop.parent
2365
2366        def proc_left_right(left, right):
2367            if isinstance(left, expression.ColumnClause) and \
2368                    isinstance(right, expression.ColumnClause):
2369                if self.child_selectable.c.contains_column(right) and \
2370                        self.parent_selectable.c.contains_column(left):
2371                    right = right._annotate({"remote": True})
2372            elif check_entities and \
2373                    right._annotations.get('parentmapper') is self.prop.mapper:
2374                right = right._annotate({"remote": True})
2375            elif check_entities and \
2376                    left._annotations.get('parentmapper') is self.prop.mapper:
2377                left = left._annotate({"remote": True})
2378            else:
2379                self._warn_non_column_elements()
2380
2381            return left, right
2382
2383        self.primaryjoin = visitors.cloned_traverse(
2384            self.primaryjoin, {},
2385            {"binary": visit_binary})
2386
2387    def _annotate_remote_distinct_selectables(self):
2388        """annotate 'remote' in primaryjoin, secondaryjoin
2389        when the parent/child tables are entirely
2390        separate.
2391
2392        """
2393        def repl(element):
2394            if self.child_selectable.c.contains_column(element) and \
2395                    (not self.parent_local_selectable.c.
2396                        contains_column(element) or
2397                        self.child_local_selectable.c.
2398                        contains_column(element)):
2399                return element._annotate({"remote": True})
2400        self.primaryjoin = visitors.replacement_traverse(
2401            self.primaryjoin, {}, repl)
2402
2403    def _warn_non_column_elements(self):
2404        util.warn(
2405            "Non-simple column elements in primary "
2406            "join condition for property %s - consider using "
2407            "remote() annotations to mark the remote side."
2408            % self.prop
2409        )
2410
2411    def _annotate_local(self):
2412        """Annotate the primaryjoin and secondaryjoin
2413        structures with 'local' annotations.
2414
2415        This annotates all column elements found
2416        simultaneously in the parent table
2417        and the join condition that don't have a
2418        'remote' annotation set up from
2419        _annotate_remote() or user-defined.
2420
2421        """
2422        if self._has_annotation(self.primaryjoin, "local"):
2423            return
2424
2425        if self._local_remote_pairs:
2426            local_side = util.column_set([l for (l, r)
2427                                          in self._local_remote_pairs])
2428        else:
2429            local_side = util.column_set(self.parent_selectable.c)
2430
2431        def locals_(elem):
2432            if "remote" not in elem._annotations and \
2433                    elem in local_side:
2434                return elem._annotate({"local": True})
2435        self.primaryjoin = visitors.replacement_traverse(
2436            self.primaryjoin, {}, locals_
2437        )
2438
2439    def _check_remote_side(self):
2440        if not self.local_remote_pairs:
2441            raise sa_exc.ArgumentError(
2442                'Relationship %s could '
2443                'not determine any unambiguous local/remote column '
2444                'pairs based on join condition and remote_side '
2445                'arguments.  '
2446                'Consider using the remote() annotation to '
2447                'accurately mark those elements of the join '
2448                'condition that are on the remote side of '
2449                'the relationship.' % (self.prop, ))
2450
2451    def _check_foreign_cols(self, join_condition, primary):
2452        """Check the foreign key columns collected and emit error
2453        messages."""
2454
2455        can_sync = False
2456
2457        foreign_cols = self._gather_columns_with_annotation(
2458            join_condition, "foreign")
2459
2460        has_foreign = bool(foreign_cols)
2461
2462        if primary:
2463            can_sync = bool(self.synchronize_pairs)
2464        else:
2465            can_sync = bool(self.secondary_synchronize_pairs)
2466
2467        if self.support_sync and can_sync or \
2468                (not self.support_sync and has_foreign):
2469            return
2470
2471        # from here below is just determining the best error message
2472        # to report.  Check for a join condition using any operator
2473        # (not just ==), perhaps they need to turn on "viewonly=True".
2474        if self.support_sync and has_foreign and not can_sync:
2475            err = "Could not locate any simple equality expressions "\
2476                "involving locally mapped foreign key columns for "\
2477                "%s join condition "\
2478                "'%s' on relationship %s." % (
2479                    primary and 'primary' or 'secondary',
2480                    join_condition,
2481                    self.prop
2482                )
2483            err += \
2484                "  Ensure that referencing columns are associated "\
2485                "with a ForeignKey or ForeignKeyConstraint, or are "\
2486                "annotated in the join condition with the foreign() "\
2487                "annotation. To allow comparison operators other than "\
2488                "'==', the relationship can be marked as viewonly=True."
2489
2490            raise sa_exc.ArgumentError(err)
2491        else:
2492            err = "Could not locate any relevant foreign key columns "\
2493                "for %s join condition '%s' on relationship %s." % (
2494                    primary and 'primary' or 'secondary',
2495                    join_condition,
2496                    self.prop
2497                )
2498            err += \
2499                '  Ensure that referencing columns are associated '\
2500                'with a ForeignKey or ForeignKeyConstraint, or are '\
2501                'annotated in the join condition with the foreign() '\
2502                'annotation.'
2503            raise sa_exc.ArgumentError(err)
2504
2505    def _determine_direction(self):
2506        """Determine if this relationship is one to many, many to one,
2507        many to many.
2508
2509        """
2510        if self.secondaryjoin is not None:
2511            self.direction = MANYTOMANY
2512        else:
2513            parentcols = util.column_set(self.parent_selectable.c)
2514            targetcols = util.column_set(self.child_selectable.c)
2515
2516            # fk collection which suggests ONETOMANY.
2517            onetomany_fk = targetcols.intersection(
2518                self.foreign_key_columns)
2519
2520            # fk collection which suggests MANYTOONE.
2521
2522            manytoone_fk = parentcols.intersection(
2523                self.foreign_key_columns)
2524
2525            if onetomany_fk and manytoone_fk:
2526                # fks on both sides.  test for overlap of local/remote
2527                # with foreign key.
2528                # we will gather columns directly from their annotations
2529                # without deannotating, so that we can distinguish on a column
2530                # that refers to itself.
2531
2532                # 1. columns that are both remote and FK suggest
2533                # onetomany.
2534                onetomany_local = self._gather_columns_with_annotation(
2535                    self.primaryjoin, "remote", "foreign")
2536
2537                # 2. columns that are FK but are not remote (e.g. local)
2538                # suggest manytoone.
2539                manytoone_local = set([c for c in
2540                                       self._gather_columns_with_annotation(
2541                                           self.primaryjoin,
2542                                           "foreign")
2543                                       if "remote" not in c._annotations])
2544
2545                # 3. if both collections are present, remove columns that
2546                # refer to themselves.  This is for the case of
2547                # and_(Me.id == Me.remote_id, Me.version == Me.version)
2548                if onetomany_local and manytoone_local:
2549                    self_equated = self.remote_columns.intersection(
2550                        self.local_columns
2551                    )
2552                    onetomany_local = onetomany_local.difference(self_equated)
2553                    manytoone_local = manytoone_local.difference(self_equated)
2554
2555                # at this point, if only one or the other collection is
2556                # present, we know the direction, otherwise it's still
2557                # ambiguous.
2558
2559                if onetomany_local and not manytoone_local:
2560                    self.direction = ONETOMANY
2561                elif manytoone_local and not onetomany_local:
2562                    self.direction = MANYTOONE
2563                else:
2564                    raise sa_exc.ArgumentError(
2565                        "Can't determine relationship"
2566                        " direction for relationship '%s' - foreign "
2567                        "key columns within the join condition are present "
2568                        "in both the parent and the child's mapped tables.  "
2569                        "Ensure that only those columns referring "
2570                        "to a parent column are marked as foreign, "
2571                        "either via the foreign() annotation or "
2572                        "via the foreign_keys argument." % self.prop)
2573            elif onetomany_fk:
2574                self.direction = ONETOMANY
2575            elif manytoone_fk:
2576                self.direction = MANYTOONE
2577            else:
2578                raise sa_exc.ArgumentError(
2579                    "Can't determine relationship "
2580                    "direction for relationship '%s' - foreign "
2581                    "key columns are present in neither the parent "
2582                    "nor the child's mapped tables" % self.prop)
2583
2584    def _deannotate_pairs(self, collection):
2585        """provide deannotation for the various lists of
2586        pairs, so that using them in hashes doesn't incur
2587        high-overhead __eq__() comparisons against
2588        original columns mapped.
2589
2590        """
2591        return [(x._deannotate(), y._deannotate())
2592                for x, y in collection]
2593
2594    def _setup_pairs(self):
2595        sync_pairs = []
2596        lrp = util.OrderedSet([])
2597        secondary_sync_pairs = []
2598
2599        def go(joincond, collection):
2600            def visit_binary(binary, left, right):
2601                if "remote" in right._annotations and \
2602                    "remote" not in left._annotations and \
2603                        self.can_be_synced_fn(left):
2604                    lrp.add((left, right))
2605                elif "remote" in left._annotations and \
2606                    "remote" not in right._annotations and \
2607                        self.can_be_synced_fn(right):
2608                    lrp.add((right, left))
2609                if binary.operator is operators.eq and \
2610                        self.can_be_synced_fn(left, right):
2611                    if "foreign" in right._annotations:
2612                        collection.append((left, right))
2613                    elif "foreign" in left._annotations:
2614                        collection.append((right, left))
2615            visit_binary_product(visit_binary, joincond)
2616
2617        for joincond, collection in [
2618            (self.primaryjoin, sync_pairs),
2619            (self.secondaryjoin, secondary_sync_pairs)
2620        ]:
2621            if joincond is None:
2622                continue
2623            go(joincond, collection)
2624
2625        self.local_remote_pairs = self._deannotate_pairs(lrp)
2626        self.synchronize_pairs = self._deannotate_pairs(sync_pairs)
2627        self.secondary_synchronize_pairs = \
2628            self._deannotate_pairs(secondary_sync_pairs)
2629
2630    _track_overlapping_sync_targets = weakref.WeakKeyDictionary()
2631
2632    def _warn_for_conflicting_sync_targets(self):
2633        if not self.support_sync:
2634            return
2635
2636        # we would like to detect if we are synchronizing any column
2637        # pairs in conflict with another relationship that wishes to sync
2638        # an entirely different column to the same target.   This is a
2639        # very rare edge case so we will try to minimize the memory/overhead
2640        # impact of this check
2641        for from_, to_ in [
2642            (from_, to_) for (from_, to_) in self.synchronize_pairs
2643        ] + [
2644            (from_, to_) for (from_, to_) in self.secondary_synchronize_pairs
2645        ]:
2646            # save ourselves a ton of memory and overhead by only
2647            # considering columns that are subject to a overlapping
2648            # FK constraints at the core level.   This condition can arise
2649            # if multiple relationships overlap foreign() directly, but
2650            # we're going to assume it's typically a ForeignKeyConstraint-
2651            # level configuration that benefits from this warning.
2652            if len(to_.foreign_keys) < 2:
2653                continue
2654
2655            if to_ not in self._track_overlapping_sync_targets:
2656                self._track_overlapping_sync_targets[to_] = \
2657                    weakref.WeakKeyDictionary({self.prop: from_})
2658            else:
2659                other_props = []
2660                prop_to_from = self._track_overlapping_sync_targets[to_]
2661                for pr, fr_ in prop_to_from.items():
2662                    if pr.mapper in mapperlib._mapper_registry and \
2663                        fr_ is not from_ and \
2664                            pr not in self.prop._reverse_property:
2665                        other_props.append((pr, fr_))
2666
2667                if other_props:
2668                    util.warn(
2669                        "relationship '%s' will copy column %s to column %s, "
2670                        "which conflicts with relationship(s): %s. "
2671                        "Consider applying "
2672                        "viewonly=True to read-only relationships, or provide "
2673                        "a primaryjoin condition marking writable columns "
2674                        "with the foreign() annotation." % (
2675                            self.prop,
2676                            from_, to_,
2677                            ", ".join(
2678                                "'%s' (copies %s to %s)" % (pr, fr_, to_)
2679                                for (pr, fr_) in other_props)
2680                        )
2681                    )
2682                self._track_overlapping_sync_targets[to_][self.prop] = from_
2683
2684    @util.memoized_property
2685    def remote_columns(self):
2686        return self._gather_join_annotations("remote")
2687
2688    @util.memoized_property
2689    def local_columns(self):
2690        return self._gather_join_annotations("local")
2691
2692    @util.memoized_property
2693    def foreign_key_columns(self):
2694        return self._gather_join_annotations("foreign")
2695
2696    @util.memoized_property
2697    def deannotated_primaryjoin(self):
2698        return _deep_deannotate(self.primaryjoin)
2699
2700    @util.memoized_property
2701    def deannotated_secondaryjoin(self):
2702        if self.secondaryjoin is not None:
2703            return _deep_deannotate(self.secondaryjoin)
2704        else:
2705            return None
2706
2707    def _gather_join_annotations(self, annotation):
2708        s = set(
2709            self._gather_columns_with_annotation(
2710                self.primaryjoin, annotation)
2711        )
2712        if self.secondaryjoin is not None:
2713            s.update(
2714                self._gather_columns_with_annotation(
2715                    self.secondaryjoin, annotation)
2716            )
2717        return set([x._deannotate() for x in s])
2718
2719    def _gather_columns_with_annotation(self, clause, *annotation):
2720        annotation = set(annotation)
2721        return set([
2722            col for col in visitors.iterate(clause, {})
2723            if annotation.issubset(col._annotations)
2724        ])
2725
2726    def join_targets(self, source_selectable,
2727                     dest_selectable,
2728                     aliased,
2729                     single_crit=None):
2730        """Given a source and destination selectable, create a
2731        join between them.
2732
2733        This takes into account aliasing the join clause
2734        to reference the appropriate corresponding columns
2735        in the target objects, as well as the extra child
2736        criterion, equivalent column sets, etc.
2737
2738        """
2739
2740        # place a barrier on the destination such that
2741        # replacement traversals won't ever dig into it.
2742        # its internal structure remains fixed
2743        # regardless of context.
2744        dest_selectable = _shallow_annotate(
2745            dest_selectable,
2746            {'no_replacement_traverse': True})
2747
2748        primaryjoin, secondaryjoin, secondary = self.primaryjoin, \
2749            self.secondaryjoin, self.secondary
2750
2751        # adjust the join condition for single table inheritance,
2752        # in the case that the join is to a subclass
2753        # this is analogous to the
2754        # "_adjust_for_single_table_inheritance()" method in Query.
2755
2756        if single_crit is not None:
2757            if secondaryjoin is not None:
2758                secondaryjoin = secondaryjoin & single_crit
2759            else:
2760                primaryjoin = primaryjoin & single_crit
2761
2762        if aliased:
2763            if secondary is not None:
2764                secondary = secondary.alias(flat=True)
2765                primary_aliasizer = ClauseAdapter(secondary)
2766                secondary_aliasizer = \
2767                    ClauseAdapter(dest_selectable,
2768                                  equivalents=self.child_equivalents).\
2769                    chain(primary_aliasizer)
2770                if source_selectable is not None:
2771                    primary_aliasizer = \
2772                        ClauseAdapter(secondary).\
2773                        chain(ClauseAdapter(
2774                            source_selectable,
2775                            equivalents=self.parent_equivalents))
2776                secondaryjoin = \
2777                    secondary_aliasizer.traverse(secondaryjoin)
2778            else:
2779                primary_aliasizer = ClauseAdapter(
2780                    dest_selectable,
2781                    exclude_fn=_ColInAnnotations("local"),
2782                    equivalents=self.child_equivalents)
2783                if source_selectable is not None:
2784                    primary_aliasizer.chain(
2785                        ClauseAdapter(source_selectable,
2786                                      exclude_fn=_ColInAnnotations("remote"),
2787                                      equivalents=self.parent_equivalents))
2788                secondary_aliasizer = None
2789
2790            primaryjoin = primary_aliasizer.traverse(primaryjoin)
2791            target_adapter = secondary_aliasizer or primary_aliasizer
2792            target_adapter.exclude_fn = None
2793        else:
2794            target_adapter = None
2795        return primaryjoin, secondaryjoin, secondary, \
2796            target_adapter, dest_selectable
2797
2798    def create_lazy_clause(self, reverse_direction=False):
2799        binds = util.column_dict()
2800        equated_columns = util.column_dict()
2801
2802        has_secondary = self.secondaryjoin is not None
2803
2804        if has_secondary:
2805            lookup = collections.defaultdict(list)
2806            for l, r in self.local_remote_pairs:
2807                lookup[l].append((l, r))
2808                equated_columns[r] = l
2809        elif not reverse_direction:
2810            for l, r in self.local_remote_pairs:
2811                equated_columns[r] = l
2812        else:
2813            for l, r in self.local_remote_pairs:
2814                equated_columns[l] = r
2815
2816        def col_to_bind(col):
2817
2818            if (
2819                (not reverse_direction and 'local' in col._annotations) or
2820                reverse_direction and (
2821                    (has_secondary and col in lookup) or
2822                    (not has_secondary and 'remote' in col._annotations)
2823                )
2824            ):
2825                if col not in binds:
2826                    binds[col] = sql.bindparam(
2827                        None, None, type_=col.type, unique=True)
2828                return binds[col]
2829            return None
2830
2831        lazywhere = self.primaryjoin
2832        if self.secondaryjoin is None or not reverse_direction:
2833            lazywhere = visitors.replacement_traverse(
2834                lazywhere, {}, col_to_bind)
2835
2836        if self.secondaryjoin is not None:
2837            secondaryjoin = self.secondaryjoin
2838            if reverse_direction:
2839                secondaryjoin = visitors.replacement_traverse(
2840                    secondaryjoin, {}, col_to_bind)
2841            lazywhere = sql.and_(lazywhere, secondaryjoin)
2842
2843        bind_to_col = dict((binds[col].key, col) for col in binds)
2844
2845        # this is probably not necessary
2846        lazywhere = _deep_deannotate(lazywhere)
2847
2848        return lazywhere, bind_to_col, equated_columns
2849
2850
2851class _ColInAnnotations(object):
2852    """Seralizable equivalent to:
2853
2854        lambda c: "name" in c._annotations
2855    """
2856
2857    def __init__(self, name):
2858        self.name = name
2859
2860    def __call__(self, c):
2861        return self.name in c._annotations
2862