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