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.2.7, created at 2023-06-07 06:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
1# orm/relationships.py
2# Copyright (C) 2005-2023 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. This flag is used to handle saving bi-directional
762 dependencies between two individual rows (i.e. each row
763 references the other), where it would otherwise be impossible to
764 INSERT or DELETE both rows fully since one row exists before the
765 other. Use this flag when a particular mapping arrangement will
766 incur two rows that are dependent on each other, such as a table
767 that has a one-to-many relationship to a set of child rows, and
768 also has a column that references a single child row within that
769 list (i.e. both tables contain a foreign key to each other). If
770 a flush operation returns an error that a "cyclical
771 dependency" was detected, this is a cue that you might want to
772 use :paramref:`_orm.relationship.post_update` to "break" the cycle.
774 .. seealso::
776 :ref:`post_update` - Introductory documentation and examples.
778 :param primaryjoin:
779 A SQL expression that will be used as the primary
780 join of the child object against the parent object, or in a
781 many-to-many relationship the join of the parent object to the
782 association table. By default, this value is computed based on the
783 foreign key relationships of the parent and child tables (or
784 association table).
786 :paramref:`_orm.relationship.primaryjoin` may also be passed as a
787 callable function which is evaluated at mapper initialization time,
788 and may be passed as a Python-evaluable string when using
789 Declarative.
791 .. warning:: When passed as a Python-evaluable string, the
792 argument is interpreted using Python's ``eval()`` function.
793 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
794 See :ref:`declarative_relationship_eval` for details on
795 declarative evaluation of :func:`_orm.relationship` arguments.
797 .. seealso::
799 :ref:`relationship_primaryjoin`
801 :param remote_side:
802 Used for self-referential relationships, indicates the column or
803 list of columns that form the "remote side" of the relationship.
805 :paramref:`_orm.relationship.remote_side` may also be passed as a
806 callable function which is evaluated at mapper initialization time,
807 and may be passed as a Python-evaluable string when using
808 Declarative.
810 .. warning:: When passed as a Python-evaluable string, the
811 argument is interpreted using Python's ``eval()`` function.
812 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
813 See :ref:`declarative_relationship_eval` for details on
814 declarative evaluation of :func:`_orm.relationship` arguments.
816 .. seealso::
818 :ref:`self_referential` - in-depth explanation of how
819 :paramref:`_orm.relationship.remote_side`
820 is used to configure self-referential relationships.
822 :func:`.remote` - an annotation function that accomplishes the
823 same purpose as :paramref:`_orm.relationship.remote_side`,
824 typically
825 when a custom :paramref:`_orm.relationship.primaryjoin` condition
826 is used.
828 :param query_class:
829 A :class:`_query.Query`
830 subclass that will be used internally by the
831 ``AppenderQuery`` returned by a "dynamic" relationship, that
832 is, a relationship that specifies ``lazy="dynamic"`` or was
833 otherwise constructed using the :func:`_orm.dynamic_loader`
834 function.
836 .. seealso::
838 :ref:`dynamic_relationship` - Introduction to "dynamic"
839 relationship loaders.
841 :param secondaryjoin:
842 A SQL expression that will be used as the join of
843 an association table to the child object. By default, this value is
844 computed based on the foreign key relationships of the association
845 and child tables.
847 :paramref:`_orm.relationship.secondaryjoin` may also be passed as a
848 callable function which is evaluated at mapper initialization time,
849 and may be passed as a Python-evaluable string when using
850 Declarative.
852 .. warning:: When passed as a Python-evaluable string, the
853 argument is interpreted using Python's ``eval()`` function.
854 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**.
855 See :ref:`declarative_relationship_eval` for details on
856 declarative evaluation of :func:`_orm.relationship` arguments.
858 .. seealso::
860 :ref:`relationship_primaryjoin`
862 :param single_parent:
863 When True, installs a validator which will prevent objects
864 from being associated with more than one parent at a time.
865 This is used for many-to-one or many-to-many relationships that
866 should be treated either as one-to-one or one-to-many. Its usage
867 is optional, except for :func:`_orm.relationship` constructs which
868 are many-to-one or many-to-many and also
869 specify the ``delete-orphan`` cascade option. The
870 :func:`_orm.relationship` construct itself will raise an error
871 instructing when this option is required.
873 .. seealso::
875 :ref:`unitofwork_cascades` - includes detail on when the
876 :paramref:`_orm.relationship.single_parent`
877 flag may be appropriate.
879 :param uselist:
880 A boolean that indicates if this property should be loaded as a
881 list or a scalar. In most cases, this value is determined
882 automatically by :func:`_orm.relationship` at mapper configuration
883 time, based on the type and direction
884 of the relationship - one to many forms a list, many to one
885 forms a scalar, many to many is a list. If a scalar is desired
886 where normally a list would be present, such as a bi-directional
887 one-to-one relationship, set :paramref:`_orm.relationship.uselist`
888 to
889 False.
891 The :paramref:`_orm.relationship.uselist`
892 flag is also available on an
893 existing :func:`_orm.relationship`
894 construct as a read-only attribute,
895 which can be used to determine if this :func:`_orm.relationship`
896 deals
897 with collections or scalar attributes::
899 >>> User.addresses.property.uselist
900 True
902 .. seealso::
904 :ref:`relationships_one_to_one` - Introduction to the "one to
905 one" relationship pattern, which is typically when the
906 :paramref:`_orm.relationship.uselist` flag is needed.
908 :param viewonly=False:
909 When set to ``True``, the relationship is used only for loading
910 objects, and not for any persistence operation. A
911 :func:`_orm.relationship` which specifies
912 :paramref:`_orm.relationship.viewonly` can work
913 with a wider range of SQL operations within the
914 :paramref:`_orm.relationship.primaryjoin` condition, including
915 operations that feature the use of a variety of comparison operators
916 as well as SQL functions such as :func:`_expression.cast`. The
917 :paramref:`_orm.relationship.viewonly`
918 flag is also of general use when defining any kind of
919 :func:`_orm.relationship` that doesn't represent
920 the full set of related objects, to prevent modifications of the
921 collection from resulting in persistence operations.
923 When using the :paramref:`_orm.relationship.viewonly` flag in
924 conjunction with backrefs, the originating relationship for a
925 particular state change will not produce state changes within the
926 viewonly relationship. This is the behavior implied by
927 :paramref:`_orm.relationship.sync_backref` being set to False.
929 .. versionchanged:: 1.3.17 - the
930 :paramref:`_orm.relationship.sync_backref` flag is set to False
931 when using viewonly in conjunction with backrefs.
933 .. seealso::
935 :paramref:`_orm.relationship.sync_backref`
937 :param sync_backref:
938 A boolean that enables the events used to synchronize the in-Python
939 attributes when this relationship is target of either
940 :paramref:`_orm.relationship.backref` or
941 :paramref:`_orm.relationship.back_populates`.
943 Defaults to ``None``, which indicates that an automatic value should
944 be selected based on the value of the
945 :paramref:`_orm.relationship.viewonly` flag. When left at its
946 default, changes in state will be back-populated only if neither
947 sides of a relationship is viewonly.
949 .. versionadded:: 1.3.17
951 .. versionchanged:: 1.4 - A relationship that specifies
952 :paramref:`_orm.relationship.viewonly` automatically implies
953 that :paramref:`_orm.relationship.sync_backref` is ``False``.
955 .. seealso::
957 :paramref:`_orm.relationship.viewonly`
959 :param omit_join:
960 Allows manual control over the "selectin" automatic join
961 optimization. Set to ``False`` to disable the "omit join" feature
962 added in SQLAlchemy 1.3; or leave as ``None`` to leave automatic
963 optimization in place.
965 .. note:: This flag may only be set to ``False``. It is not
966 necessary to set it to ``True`` as the "omit_join" optimization is
967 automatically detected; if it is not detected, then the
968 optimization is not supported.
970 .. versionchanged:: 1.3.11 setting ``omit_join`` to True will now
971 emit a warning as this was not the intended use of this flag.
973 .. versionadded:: 1.3
976 """
977 super(RelationshipProperty, self).__init__()
979 self.uselist = uselist
980 self.argument = argument
981 self.secondary = secondary
982 self.primaryjoin = primaryjoin
983 self.secondaryjoin = secondaryjoin
984 self.post_update = post_update
985 self.direction = None
986 self.viewonly = viewonly
987 if viewonly:
988 self._warn_for_persistence_only_flags(
989 passive_deletes=passive_deletes,
990 passive_updates=passive_updates,
991 enable_typechecks=enable_typechecks,
992 active_history=active_history,
993 cascade_backrefs=cascade_backrefs,
994 )
995 if viewonly and sync_backref:
996 raise sa_exc.ArgumentError(
997 "sync_backref and viewonly cannot both be True"
998 )
999 self.sync_backref = sync_backref
1000 self.lazy = lazy
1001 self.single_parent = single_parent
1002 self._user_defined_foreign_keys = foreign_keys
1003 self.collection_class = collection_class
1004 self.passive_deletes = passive_deletes
1005 self.cascade_backrefs = cascade_backrefs
1006 self.passive_updates = passive_updates
1007 self.remote_side = remote_side
1008 self.enable_typechecks = enable_typechecks
1009 self.query_class = query_class
1010 self.innerjoin = innerjoin
1011 self.distinct_target_key = distinct_target_key
1012 self.doc = doc
1013 self.active_history = active_history
1014 self._legacy_inactive_history_style = _legacy_inactive_history_style
1016 self.join_depth = join_depth
1017 if omit_join:
1018 util.warn(
1019 "setting omit_join to True is not supported; selectin "
1020 "loading of this relationship may not work correctly if this "
1021 "flag is set explicitly. omit_join optimization is "
1022 "automatically detected for conditions under which it is "
1023 "supported."
1024 )
1026 self.omit_join = omit_join
1027 self.local_remote_pairs = _local_remote_pairs
1028 self.bake_queries = bake_queries
1029 self.load_on_pending = load_on_pending
1030 self.comparator_factory = (
1031 comparator_factory or RelationshipProperty.Comparator
1032 )
1033 self.comparator = self.comparator_factory(self, None)
1034 util.set_creation_order(self)
1036 if info is not None:
1037 self.info = info
1039 self.strategy_key = (("lazy", self.lazy),)
1041 self._reverse_property = set()
1042 if overlaps:
1043 self._overlaps = set(re.split(r"\s*,\s*", overlaps))
1044 else:
1045 self._overlaps = ()
1047 if cascade is not False:
1048 self.cascade = cascade
1049 elif self.viewonly:
1050 self.cascade = "merge"
1051 else:
1052 self.cascade = "save-update, merge"
1054 self.order_by = order_by
1056 self.back_populates = back_populates
1058 if self.back_populates:
1059 if backref:
1060 raise sa_exc.ArgumentError(
1061 "backref and back_populates keyword arguments "
1062 "are mutually exclusive"
1063 )
1064 self.backref = None
1065 else:
1066 self.backref = backref
1068 def _warn_for_persistence_only_flags(self, **kw):
1069 for k, v in kw.items():
1070 if v != self._persistence_only[k]:
1071 # we are warning here rather than warn deprecated as this is a
1072 # configuration mistake, and Python shows regular warnings more
1073 # aggressively than deprecation warnings by default. Unlike the
1074 # case of setting viewonly with cascade, the settings being
1075 # warned about here are not actively doing the wrong thing
1076 # against viewonly=True, so it is not as urgent to have these
1077 # raise an error.
1078 util.warn(
1079 "Setting %s on relationship() while also "
1080 "setting viewonly=True does not make sense, as a "
1081 "viewonly=True relationship does not perform persistence "
1082 "operations. This configuration may raise an error "
1083 "in a future release." % (k,)
1084 )
1086 def instrument_class(self, mapper):
1087 attributes.register_descriptor(
1088 mapper.class_,
1089 self.key,
1090 comparator=self.comparator_factory(self, mapper),
1091 parententity=mapper,
1092 doc=self.doc,
1093 )
1095 class Comparator(PropComparator):
1096 """Produce boolean, comparison, and other operators for
1097 :class:`.RelationshipProperty` attributes.
1099 See the documentation for :class:`.PropComparator` for a brief
1100 overview of ORM level operator definition.
1102 .. seealso::
1104 :class:`.PropComparator`
1106 :class:`.ColumnProperty.Comparator`
1108 :class:`.ColumnOperators`
1110 :ref:`types_operators`
1112 :attr:`.TypeEngine.comparator_factory`
1114 """
1116 _of_type = None
1117 _extra_criteria = ()
1119 def __init__(
1120 self,
1121 prop,
1122 parentmapper,
1123 adapt_to_entity=None,
1124 of_type=None,
1125 extra_criteria=(),
1126 ):
1127 """Construction of :class:`.RelationshipProperty.Comparator`
1128 is internal to the ORM's attribute mechanics.
1130 """
1131 self.prop = prop
1132 self._parententity = parentmapper
1133 self._adapt_to_entity = adapt_to_entity
1134 if of_type:
1135 self._of_type = of_type
1136 self._extra_criteria = extra_criteria
1138 def adapt_to_entity(self, adapt_to_entity):
1139 return self.__class__(
1140 self.property,
1141 self._parententity,
1142 adapt_to_entity=adapt_to_entity,
1143 of_type=self._of_type,
1144 )
1146 @util.memoized_property
1147 def entity(self):
1148 """The target entity referred to by this
1149 :class:`.RelationshipProperty.Comparator`.
1151 This is either a :class:`_orm.Mapper` or :class:`.AliasedInsp`
1152 object.
1154 This is the "target" or "remote" side of the
1155 :func:`_orm.relationship`.
1157 """
1158 # this is a relatively recent change made for
1159 # 1.4.27 as part of #7244.
1160 # TODO: shouldn't _of_type be inspected up front when received?
1161 if self._of_type is not None:
1162 return inspect(self._of_type)
1163 else:
1164 return self.property.entity
1166 @util.memoized_property
1167 def mapper(self):
1168 """The target :class:`_orm.Mapper` referred to by this
1169 :class:`.RelationshipProperty.Comparator`.
1171 This is the "target" or "remote" side of the
1172 :func:`_orm.relationship`.
1174 """
1175 return self.property.mapper
1177 @util.memoized_property
1178 def _parententity(self):
1179 return self.property.parent
1181 def _source_selectable(self):
1182 if self._adapt_to_entity:
1183 return self._adapt_to_entity.selectable
1184 else:
1185 return self.property.parent._with_polymorphic_selectable
1187 def __clause_element__(self):
1188 adapt_from = self._source_selectable()
1189 if self._of_type:
1190 of_type_entity = inspect(self._of_type)
1191 else:
1192 of_type_entity = None
1194 (
1195 pj,
1196 sj,
1197 source,
1198 dest,
1199 secondary,
1200 target_adapter,
1201 ) = self.property._create_joins(
1202 source_selectable=adapt_from,
1203 source_polymorphic=True,
1204 of_type_entity=of_type_entity,
1205 alias_secondary=True,
1206 extra_criteria=self._extra_criteria,
1207 )
1208 if sj is not None:
1209 return pj & sj
1210 else:
1211 return pj
1213 def of_type(self, cls):
1214 r"""Redefine this object in terms of a polymorphic subclass.
1216 See :meth:`.PropComparator.of_type` for an example.
1219 """
1220 return RelationshipProperty.Comparator(
1221 self.property,
1222 self._parententity,
1223 adapt_to_entity=self._adapt_to_entity,
1224 of_type=cls,
1225 extra_criteria=self._extra_criteria,
1226 )
1228 def and_(self, *other):
1229 """Add AND criteria.
1231 See :meth:`.PropComparator.and_` for an example.
1233 .. versionadded:: 1.4
1235 """
1236 return RelationshipProperty.Comparator(
1237 self.property,
1238 self._parententity,
1239 adapt_to_entity=self._adapt_to_entity,
1240 of_type=self._of_type,
1241 extra_criteria=self._extra_criteria + other,
1242 )
1244 def in_(self, other):
1245 """Produce an IN clause - this is not implemented
1246 for :func:`_orm.relationship`-based attributes at this time.
1248 """
1249 raise NotImplementedError(
1250 "in_() not yet supported for "
1251 "relationships. For a simple "
1252 "many-to-one, use in_() against "
1253 "the set of foreign key values."
1254 )
1256 __hash__ = None
1258 def __eq__(self, other):
1259 """Implement the ``==`` operator.
1261 In a many-to-one context, such as::
1263 MyClass.some_prop == <some object>
1265 this will typically produce a
1266 clause such as::
1268 mytable.related_id == <some id>
1270 Where ``<some id>`` is the primary key of the given
1271 object.
1273 The ``==`` operator provides partial functionality for non-
1274 many-to-one comparisons:
1276 * Comparisons against collections are not supported.
1277 Use :meth:`~.RelationshipProperty.Comparator.contains`.
1278 * Compared to a scalar one-to-many, will produce a
1279 clause that compares the target columns in the parent to
1280 the given target.
1281 * Compared to a scalar many-to-many, an alias
1282 of the association table will be rendered as
1283 well, forming a natural join that is part of the
1284 main body of the query. This will not work for
1285 queries that go beyond simple AND conjunctions of
1286 comparisons, such as those which use OR. Use
1287 explicit joins, outerjoins, or
1288 :meth:`~.RelationshipProperty.Comparator.has` for
1289 more comprehensive non-many-to-one scalar
1290 membership tests.
1291 * Comparisons against ``None`` given in a one-to-many
1292 or many-to-many context produce a NOT EXISTS clause.
1294 """
1295 if isinstance(other, (util.NoneType, expression.Null)):
1296 if self.property.direction in [ONETOMANY, MANYTOMANY]:
1297 return ~self._criterion_exists()
1298 else:
1299 return _orm_annotate(
1300 self.property._optimized_compare(
1301 None, adapt_source=self.adapter
1302 )
1303 )
1304 elif self.property.uselist:
1305 raise sa_exc.InvalidRequestError(
1306 "Can't compare a collection to an object or collection; "
1307 "use contains() to test for membership."
1308 )
1309 else:
1310 return _orm_annotate(
1311 self.property._optimized_compare(
1312 other, adapt_source=self.adapter
1313 )
1314 )
1316 def _criterion_exists(self, criterion=None, **kwargs):
1317 if getattr(self, "_of_type", None):
1318 info = inspect(self._of_type)
1319 target_mapper, to_selectable, is_aliased_class = (
1320 info.mapper,
1321 info.selectable,
1322 info.is_aliased_class,
1323 )
1324 if self.property._is_self_referential and not is_aliased_class:
1325 to_selectable = to_selectable._anonymous_fromclause()
1327 single_crit = target_mapper._single_table_criterion
1328 if single_crit is not None:
1329 if criterion is not None:
1330 criterion = single_crit & criterion
1331 else:
1332 criterion = single_crit
1333 else:
1334 is_aliased_class = False
1335 to_selectable = None
1337 if self.adapter:
1338 source_selectable = self._source_selectable()
1339 else:
1340 source_selectable = None
1342 (
1343 pj,
1344 sj,
1345 source,
1346 dest,
1347 secondary,
1348 target_adapter,
1349 ) = self.property._create_joins(
1350 dest_selectable=to_selectable,
1351 source_selectable=source_selectable,
1352 )
1354 for k in kwargs:
1355 crit = getattr(self.property.mapper.class_, k) == kwargs[k]
1356 if criterion is None:
1357 criterion = crit
1358 else:
1359 criterion = criterion & crit
1361 # annotate the *local* side of the join condition, in the case
1362 # of pj + sj this is the full primaryjoin, in the case of just
1363 # pj its the local side of the primaryjoin.
1364 if sj is not None:
1365 j = _orm_annotate(pj) & sj
1366 else:
1367 j = _orm_annotate(pj, exclude=self.property.remote_side)
1369 if (
1370 criterion is not None
1371 and target_adapter
1372 and not is_aliased_class
1373 ):
1374 # limit this adapter to annotated only?
1375 criterion = target_adapter.traverse(criterion)
1377 # only have the "joined left side" of what we
1378 # return be subject to Query adaption. The right
1379 # side of it is used for an exists() subquery and
1380 # should not correlate or otherwise reach out
1381 # to anything in the enclosing query.
1382 if criterion is not None:
1383 criterion = criterion._annotate(
1384 {"no_replacement_traverse": True}
1385 )
1387 crit = j & sql.True_._ifnone(criterion)
1389 if secondary is not None:
1390 ex = (
1391 sql.exists(1)
1392 .where(crit)
1393 .select_from(dest, secondary)
1394 .correlate_except(dest, secondary)
1395 )
1396 else:
1397 ex = (
1398 sql.exists(1)
1399 .where(crit)
1400 .select_from(dest)
1401 .correlate_except(dest)
1402 )
1403 return ex
1405 def any(self, criterion=None, **kwargs):
1406 """Produce an expression that tests a collection against
1407 particular criterion, using EXISTS.
1409 An expression like::
1411 session.query(MyClass).filter(
1412 MyClass.somereference.any(SomeRelated.x==2)
1413 )
1416 Will produce a query like::
1418 SELECT * FROM my_table WHERE
1419 EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id
1420 AND related.x=2)
1422 Because :meth:`~.RelationshipProperty.Comparator.any` uses
1423 a correlated subquery, its performance is not nearly as
1424 good when compared against large target tables as that of
1425 using a join.
1427 :meth:`~.RelationshipProperty.Comparator.any` is particularly
1428 useful for testing for empty collections::
1430 session.query(MyClass).filter(
1431 ~MyClass.somereference.any()
1432 )
1434 will produce::
1436 SELECT * FROM my_table WHERE
1437 NOT (EXISTS (SELECT 1 FROM related WHERE
1438 related.my_id=my_table.id))
1440 :meth:`~.RelationshipProperty.Comparator.any` is only
1441 valid for collections, i.e. a :func:`_orm.relationship`
1442 that has ``uselist=True``. For scalar references,
1443 use :meth:`~.RelationshipProperty.Comparator.has`.
1445 """
1446 if not self.property.uselist:
1447 raise sa_exc.InvalidRequestError(
1448 "'any()' not implemented for scalar "
1449 "attributes. Use has()."
1450 )
1452 return self._criterion_exists(criterion, **kwargs)
1454 def has(self, criterion=None, **kwargs):
1455 """Produce an expression that tests a scalar reference against
1456 particular criterion, using EXISTS.
1458 An expression like::
1460 session.query(MyClass).filter(
1461 MyClass.somereference.has(SomeRelated.x==2)
1462 )
1465 Will produce a query like::
1467 SELECT * FROM my_table WHERE
1468 EXISTS (SELECT 1 FROM related WHERE
1469 related.id==my_table.related_id AND related.x=2)
1471 Because :meth:`~.RelationshipProperty.Comparator.has` uses
1472 a correlated subquery, its performance is not nearly as
1473 good when compared against large target tables as that of
1474 using a join.
1476 :meth:`~.RelationshipProperty.Comparator.has` is only
1477 valid for scalar references, i.e. a :func:`_orm.relationship`
1478 that has ``uselist=False``. For collection references,
1479 use :meth:`~.RelationshipProperty.Comparator.any`.
1481 """
1482 if self.property.uselist:
1483 raise sa_exc.InvalidRequestError(
1484 "'has()' not implemented for collections. " "Use any()."
1485 )
1486 return self._criterion_exists(criterion, **kwargs)
1488 def contains(self, other, **kwargs):
1489 """Return a simple expression that tests a collection for
1490 containment of a particular item.
1492 :meth:`~.RelationshipProperty.Comparator.contains` is
1493 only valid for a collection, i.e. a
1494 :func:`_orm.relationship` that implements
1495 one-to-many or many-to-many with ``uselist=True``.
1497 When used in a simple one-to-many context, an
1498 expression like::
1500 MyClass.contains(other)
1502 Produces a clause like::
1504 mytable.id == <some id>
1506 Where ``<some id>`` is the value of the foreign key
1507 attribute on ``other`` which refers to the primary
1508 key of its parent object. From this it follows that
1509 :meth:`~.RelationshipProperty.Comparator.contains` is
1510 very useful when used with simple one-to-many
1511 operations.
1513 For many-to-many operations, the behavior of
1514 :meth:`~.RelationshipProperty.Comparator.contains`
1515 has more caveats. The association table will be
1516 rendered in the statement, producing an "implicit"
1517 join, that is, includes multiple tables in the FROM
1518 clause which are equated in the WHERE clause::
1520 query(MyClass).filter(MyClass.contains(other))
1522 Produces a query like::
1524 SELECT * FROM my_table, my_association_table AS
1525 my_association_table_1 WHERE
1526 my_table.id = my_association_table_1.parent_id
1527 AND my_association_table_1.child_id = <some id>
1529 Where ``<some id>`` would be the primary key of
1530 ``other``. From the above, it is clear that
1531 :meth:`~.RelationshipProperty.Comparator.contains`
1532 will **not** work with many-to-many collections when
1533 used in queries that move beyond simple AND
1534 conjunctions, such as multiple
1535 :meth:`~.RelationshipProperty.Comparator.contains`
1536 expressions joined by OR. In such cases subqueries or
1537 explicit "outer joins" will need to be used instead.
1538 See :meth:`~.RelationshipProperty.Comparator.any` for
1539 a less-performant alternative using EXISTS, or refer
1540 to :meth:`_query.Query.outerjoin`
1541 as well as :ref:`orm_queryguide_joins`
1542 for more details on constructing outer joins.
1544 kwargs may be ignored by this operator but are required for API
1545 conformance.
1546 """
1547 if not self.property.uselist:
1548 raise sa_exc.InvalidRequestError(
1549 "'contains' not implemented for scalar "
1550 "attributes. Use =="
1551 )
1552 clause = self.property._optimized_compare(
1553 other, adapt_source=self.adapter
1554 )
1556 if self.property.secondaryjoin is not None:
1557 clause.negation_clause = self.__negated_contains_or_equals(
1558 other
1559 )
1561 return clause
1563 def __negated_contains_or_equals(self, other):
1564 if self.property.direction == MANYTOONE:
1565 state = attributes.instance_state(other)
1567 def state_bindparam(local_col, state, remote_col):
1568 dict_ = state.dict
1569 return sql.bindparam(
1570 local_col.key,
1571 type_=local_col.type,
1572 unique=True,
1573 callable_=self.property._get_attr_w_warn_on_none(
1574 self.property.mapper, state, dict_, remote_col
1575 ),
1576 )
1578 def adapt(col):
1579 if self.adapter:
1580 return self.adapter(col)
1581 else:
1582 return col
1584 if self.property._use_get:
1585 return sql.and_(
1586 *[
1587 sql.or_(
1588 adapt(x)
1589 != state_bindparam(adapt(x), state, y),
1590 adapt(x) == None,
1591 )
1592 for (x, y) in self.property.local_remote_pairs
1593 ]
1594 )
1596 criterion = sql.and_(
1597 *[
1598 x == y
1599 for (x, y) in zip(
1600 self.property.mapper.primary_key,
1601 self.property.mapper.primary_key_from_instance(other),
1602 )
1603 ]
1604 )
1606 return ~self._criterion_exists(criterion)
1608 def __ne__(self, other):
1609 """Implement the ``!=`` operator.
1611 In a many-to-one context, such as::
1613 MyClass.some_prop != <some object>
1615 This will typically produce a clause such as::
1617 mytable.related_id != <some id>
1619 Where ``<some id>`` is the primary key of the
1620 given object.
1622 The ``!=`` operator provides partial functionality for non-
1623 many-to-one comparisons:
1625 * Comparisons against collections are not supported.
1626 Use
1627 :meth:`~.RelationshipProperty.Comparator.contains`
1628 in conjunction with :func:`_expression.not_`.
1629 * Compared to a scalar one-to-many, will produce a
1630 clause that compares the target columns in the parent to
1631 the given target.
1632 * Compared to a scalar many-to-many, an alias
1633 of the association table will be rendered as
1634 well, forming a natural join that is part of the
1635 main body of the query. This will not work for
1636 queries that go beyond simple AND conjunctions of
1637 comparisons, such as those which use OR. Use
1638 explicit joins, outerjoins, or
1639 :meth:`~.RelationshipProperty.Comparator.has` in
1640 conjunction with :func:`_expression.not_` for
1641 more comprehensive non-many-to-one scalar
1642 membership tests.
1643 * Comparisons against ``None`` given in a one-to-many
1644 or many-to-many context produce an EXISTS clause.
1646 """
1647 if isinstance(other, (util.NoneType, expression.Null)):
1648 if self.property.direction == MANYTOONE:
1649 return _orm_annotate(
1650 ~self.property._optimized_compare(
1651 None, adapt_source=self.adapter
1652 )
1653 )
1655 else:
1656 return self._criterion_exists()
1657 elif self.property.uselist:
1658 raise sa_exc.InvalidRequestError(
1659 "Can't compare a collection"
1660 " to an object or collection; use "
1661 "contains() to test for membership."
1662 )
1663 else:
1664 return _orm_annotate(self.__negated_contains_or_equals(other))
1666 @util.memoized_property
1667 def property(self):
1668 self.prop.parent._check_configure()
1669 return self.prop
1671 def _with_parent(self, instance, alias_secondary=True, from_entity=None):
1672 assert instance is not None
1673 adapt_source = None
1674 if from_entity is not None:
1675 insp = inspect(from_entity)
1676 if insp.is_aliased_class:
1677 adapt_source = insp._adapter.adapt_clause
1678 return self._optimized_compare(
1679 instance,
1680 value_is_parent=True,
1681 adapt_source=adapt_source,
1682 alias_secondary=alias_secondary,
1683 )
1685 def _optimized_compare(
1686 self,
1687 state,
1688 value_is_parent=False,
1689 adapt_source=None,
1690 alias_secondary=True,
1691 ):
1692 if state is not None:
1693 try:
1694 state = inspect(state)
1695 except sa_exc.NoInspectionAvailable:
1696 state = None
1698 if state is None or not getattr(state, "is_instance", False):
1699 raise sa_exc.ArgumentError(
1700 "Mapped instance expected for relationship "
1701 "comparison to object. Classes, queries and other "
1702 "SQL elements are not accepted in this context; for "
1703 "comparison with a subquery, "
1704 "use %s.has(**criteria)." % self
1705 )
1706 reverse_direction = not value_is_parent
1708 if state is None:
1709 return self._lazy_none_clause(
1710 reverse_direction, adapt_source=adapt_source
1711 )
1713 if not reverse_direction:
1714 criterion, bind_to_col = (
1715 self._lazy_strategy._lazywhere,
1716 self._lazy_strategy._bind_to_col,
1717 )
1718 else:
1719 criterion, bind_to_col = (
1720 self._lazy_strategy._rev_lazywhere,
1721 self._lazy_strategy._rev_bind_to_col,
1722 )
1724 if reverse_direction:
1725 mapper = self.mapper
1726 else:
1727 mapper = self.parent
1729 dict_ = attributes.instance_dict(state.obj())
1731 def visit_bindparam(bindparam):
1732 if bindparam._identifying_key in bind_to_col:
1733 bindparam.callable = self._get_attr_w_warn_on_none(
1734 mapper,
1735 state,
1736 dict_,
1737 bind_to_col[bindparam._identifying_key],
1738 )
1740 if self.secondary is not None and alias_secondary:
1741 criterion = ClauseAdapter(
1742 self.secondary._anonymous_fromclause()
1743 ).traverse(criterion)
1745 criterion = visitors.cloned_traverse(
1746 criterion, {}, {"bindparam": visit_bindparam}
1747 )
1749 if adapt_source:
1750 criterion = adapt_source(criterion)
1751 return criterion
1753 def _get_attr_w_warn_on_none(self, mapper, state, dict_, column):
1754 """Create the callable that is used in a many-to-one expression.
1756 E.g.::
1758 u1 = s.query(User).get(5)
1760 expr = Address.user == u1
1762 Above, the SQL should be "address.user_id = 5". The callable
1763 returned by this method produces the value "5" based on the identity
1764 of ``u1``.
1766 """
1768 # in this callable, we're trying to thread the needle through
1769 # a wide variety of scenarios, including:
1770 #
1771 # * the object hasn't been flushed yet and there's no value for
1772 # the attribute as of yet
1773 #
1774 # * the object hasn't been flushed yet but it has a user-defined
1775 # value
1776 #
1777 # * the object has a value but it's expired and not locally present
1778 #
1779 # * the object has a value but it's expired and not locally present,
1780 # and the object is also detached
1781 #
1782 # * The object hadn't been flushed yet, there was no value, but
1783 # later, the object has been expired and detached, and *now*
1784 # they're trying to evaluate it
1785 #
1786 # * the object had a value, but it was changed to a new value, and
1787 # then expired
1788 #
1789 # * the object had a value, but it was changed to a new value, and
1790 # then expired, then the object was detached
1791 #
1792 # * the object has a user-set value, but it's None and we don't do
1793 # the comparison correctly for that so warn
1794 #
1796 prop = mapper.get_property_by_column(column)
1798 # by invoking this method, InstanceState will track the last known
1799 # value for this key each time the attribute is to be expired.
1800 # this feature was added explicitly for use in this method.
1801 state._track_last_known_value(prop.key)
1803 def _go():
1804 last_known = to_return = state._last_known_values[prop.key]
1805 existing_is_available = last_known is not attributes.NO_VALUE
1807 # we support that the value may have changed. so here we
1808 # try to get the most recent value including re-fetching.
1809 # only if we can't get a value now due to detachment do we return
1810 # the last known value
1811 current_value = mapper._get_state_attr_by_column(
1812 state,
1813 dict_,
1814 column,
1815 passive=attributes.PASSIVE_OFF
1816 if state.persistent
1817 else attributes.PASSIVE_NO_FETCH ^ attributes.INIT_OK,
1818 )
1820 if current_value is attributes.NEVER_SET:
1821 if not existing_is_available:
1822 raise sa_exc.InvalidRequestError(
1823 "Can't resolve value for column %s on object "
1824 "%s; no value has been set for this column"
1825 % (column, state_str(state))
1826 )
1827 elif current_value is attributes.PASSIVE_NO_RESULT:
1828 if not existing_is_available:
1829 raise sa_exc.InvalidRequestError(
1830 "Can't resolve value for column %s on object "
1831 "%s; the object is detached and the value was "
1832 "expired" % (column, state_str(state))
1833 )
1834 else:
1835 to_return = current_value
1836 if to_return is None:
1837 util.warn(
1838 "Got None for value of column %s; this is unsupported "
1839 "for a relationship comparison and will not "
1840 "currently produce an IS comparison "
1841 "(but may in a future release)" % column
1842 )
1843 return to_return
1845 return _go
1847 def _lazy_none_clause(self, reverse_direction=False, adapt_source=None):
1848 if not reverse_direction:
1849 criterion, bind_to_col = (
1850 self._lazy_strategy._lazywhere,
1851 self._lazy_strategy._bind_to_col,
1852 )
1853 else:
1854 criterion, bind_to_col = (
1855 self._lazy_strategy._rev_lazywhere,
1856 self._lazy_strategy._rev_bind_to_col,
1857 )
1859 criterion = adapt_criterion_to_null(criterion, bind_to_col)
1861 if adapt_source:
1862 criterion = adapt_source(criterion)
1863 return criterion
1865 def __str__(self):
1866 return str(self.parent.class_.__name__) + "." + self.key
1868 def merge(
1869 self,
1870 session,
1871 source_state,
1872 source_dict,
1873 dest_state,
1874 dest_dict,
1875 load,
1876 _recursive,
1877 _resolve_conflict_map,
1878 ):
1880 if load:
1881 for r in self._reverse_property:
1882 if (source_state, r) in _recursive:
1883 return
1885 if "merge" not in self._cascade:
1886 return
1888 if self.key not in source_dict:
1889 return
1891 if self.uselist:
1892 impl = source_state.get_impl(self.key)
1893 instances_iterable = impl.get_collection(source_state, source_dict)
1895 # if this is a CollectionAttributeImpl, then empty should
1896 # be False, otherwise "self.key in source_dict" should not be
1897 # True
1898 assert not instances_iterable.empty if impl.collection else True
1900 if load:
1901 # for a full merge, pre-load the destination collection,
1902 # so that individual _merge of each item pulls from identity
1903 # map for those already present.
1904 # also assumes CollectionAttributeImpl behavior of loading
1905 # "old" list in any case
1906 dest_state.get_impl(self.key).get(
1907 dest_state, dest_dict, passive=PASSIVE_MERGE
1908 )
1910 dest_list = []
1911 for current in instances_iterable:
1912 current_state = attributes.instance_state(current)
1913 current_dict = attributes.instance_dict(current)
1914 _recursive[(current_state, self)] = True
1915 obj = session._merge(
1916 current_state,
1917 current_dict,
1918 load=load,
1919 _recursive=_recursive,
1920 _resolve_conflict_map=_resolve_conflict_map,
1921 )
1922 if obj is not None:
1923 dest_list.append(obj)
1925 if not load:
1926 coll = attributes.init_state_collection(
1927 dest_state, dest_dict, self.key
1928 )
1929 for c in dest_list:
1930 coll.append_without_event(c)
1931 else:
1932 dest_state.get_impl(self.key).set(
1933 dest_state,
1934 dest_dict,
1935 dest_list,
1936 _adapt=False,
1937 passive=PASSIVE_MERGE,
1938 )
1939 else:
1940 current = source_dict[self.key]
1941 if current is not None:
1942 current_state = attributes.instance_state(current)
1943 current_dict = attributes.instance_dict(current)
1944 _recursive[(current_state, self)] = True
1945 obj = session._merge(
1946 current_state,
1947 current_dict,
1948 load=load,
1949 _recursive=_recursive,
1950 _resolve_conflict_map=_resolve_conflict_map,
1951 )
1952 else:
1953 obj = None
1955 if not load:
1956 dest_dict[self.key] = obj
1957 else:
1958 dest_state.get_impl(self.key).set(
1959 dest_state, dest_dict, obj, None
1960 )
1962 def _value_as_iterable(
1963 self, state, dict_, key, passive=attributes.PASSIVE_OFF
1964 ):
1965 """Return a list of tuples (state, obj) for the given
1966 key.
1968 returns an empty list if the value is None/empty/PASSIVE_NO_RESULT
1969 """
1971 impl = state.manager[key].impl
1972 x = impl.get(state, dict_, passive=passive)
1973 if x is attributes.PASSIVE_NO_RESULT or x is None:
1974 return []
1975 elif hasattr(impl, "get_collection"):
1976 return [
1977 (attributes.instance_state(o), o)
1978 for o in impl.get_collection(state, dict_, x, passive=passive)
1979 ]
1980 else:
1981 return [(attributes.instance_state(x), x)]
1983 def cascade_iterator(
1984 self, type_, state, dict_, visited_states, halt_on=None
1985 ):
1986 # assert type_ in self._cascade
1988 # only actively lazy load on the 'delete' cascade
1989 if type_ != "delete" or self.passive_deletes:
1990 passive = attributes.PASSIVE_NO_INITIALIZE
1991 else:
1992 passive = attributes.PASSIVE_OFF
1994 if type_ == "save-update":
1995 tuples = state.manager[self.key].impl.get_all_pending(state, dict_)
1997 else:
1998 tuples = self._value_as_iterable(
1999 state, dict_, self.key, passive=passive
2000 )
2002 skip_pending = (
2003 type_ == "refresh-expire" and "delete-orphan" not in self._cascade
2004 )
2006 for instance_state, c in tuples:
2007 if instance_state in visited_states:
2008 continue
2010 if c is None:
2011 # would like to emit a warning here, but
2012 # would not be consistent with collection.append(None)
2013 # current behavior of silently skipping.
2014 # see [ticket:2229]
2015 continue
2017 instance_dict = attributes.instance_dict(c)
2019 if halt_on and halt_on(instance_state):
2020 continue
2022 if skip_pending and not instance_state.key:
2023 continue
2025 instance_mapper = instance_state.manager.mapper
2027 if not instance_mapper.isa(self.mapper.class_manager.mapper):
2028 raise AssertionError(
2029 "Attribute '%s' on class '%s' "
2030 "doesn't handle objects "
2031 "of type '%s'"
2032 % (self.key, self.parent.class_, c.__class__)
2033 )
2035 visited_states.add(instance_state)
2037 yield c, instance_mapper, instance_state, instance_dict
2039 @property
2040 def _effective_sync_backref(self):
2041 if self.viewonly:
2042 return False
2043 else:
2044 return self.sync_backref is not False
2046 @staticmethod
2047 def _check_sync_backref(rel_a, rel_b):
2048 if rel_a.viewonly and rel_b.sync_backref:
2049 raise sa_exc.InvalidRequestError(
2050 "Relationship %s cannot specify sync_backref=True since %s "
2051 "includes viewonly=True." % (rel_b, rel_a)
2052 )
2053 if (
2054 rel_a.viewonly
2055 and not rel_b.viewonly
2056 and rel_b.sync_backref is not False
2057 ):
2058 rel_b.sync_backref = False
2060 def _add_reverse_property(self, key):
2061 other = self.mapper.get_property(key, _configure_mappers=False)
2062 if not isinstance(other, RelationshipProperty):
2063 raise sa_exc.InvalidRequestError(
2064 "back_populates on relationship '%s' refers to attribute '%s' "
2065 "that is not a relationship. The back_populates parameter "
2066 "should refer to the name of a relationship on the target "
2067 "class." % (self, other)
2068 )
2069 # viewonly and sync_backref cases
2070 # 1. self.viewonly==True and other.sync_backref==True -> error
2071 # 2. self.viewonly==True and other.viewonly==False and
2072 # other.sync_backref==None -> warn sync_backref=False, set to False
2073 self._check_sync_backref(self, other)
2074 # 3. other.viewonly==True and self.sync_backref==True -> error
2075 # 4. other.viewonly==True and self.viewonly==False and
2076 # self.sync_backref==None -> warn sync_backref=False, set to False
2077 self._check_sync_backref(other, self)
2079 self._reverse_property.add(other)
2080 other._reverse_property.add(self)
2082 if not other.mapper.common_parent(self.parent):
2083 raise sa_exc.ArgumentError(
2084 "reverse_property %r on "
2085 "relationship %s references relationship %s, which "
2086 "does not reference mapper %s"
2087 % (key, self, other, self.parent)
2088 )
2090 if (
2091 self.direction in (ONETOMANY, MANYTOONE)
2092 and self.direction == other.direction
2093 ):
2094 raise sa_exc.ArgumentError(
2095 "%s and back-reference %s are "
2096 "both of the same direction %r. Did you mean to "
2097 "set remote_side on the many-to-one side ?"
2098 % (other, self, self.direction)
2099 )
2101 @util.memoized_property
2102 @util.preload_module("sqlalchemy.orm.mapper")
2103 def entity(self):
2104 """Return the target mapped entity, which is an inspect() of the
2105 class or aliased class that is referred towards.
2107 """
2109 mapperlib = util.preloaded.orm_mapper
2111 if isinstance(self.argument, util.string_types):
2112 argument = self._clsregistry_resolve_name(self.argument)()
2114 elif callable(self.argument) and not isinstance(
2115 self.argument, (type, mapperlib.Mapper)
2116 ):
2117 argument = self.argument()
2118 else:
2119 argument = self.argument
2121 if isinstance(argument, type):
2122 return mapperlib.class_mapper(argument, configure=False)
2124 try:
2125 entity = inspect(argument)
2126 except sa_exc.NoInspectionAvailable:
2127 pass
2128 else:
2129 if hasattr(entity, "mapper"):
2130 return entity
2132 raise sa_exc.ArgumentError(
2133 "relationship '%s' expects "
2134 "a class or a mapper argument (received: %s)"
2135 % (self.key, type(argument))
2136 )
2138 @util.memoized_property
2139 def mapper(self):
2140 """Return the targeted :class:`_orm.Mapper` for this
2141 :class:`.RelationshipProperty`.
2143 This is a lazy-initializing static attribute.
2145 """
2146 return self.entity.mapper
2148 def do_init(self):
2149 self._check_conflicts()
2150 self._process_dependent_arguments()
2151 self._setup_registry_dependencies()
2152 self._setup_join_conditions()
2153 self._check_cascade_settings(self._cascade)
2154 self._post_init()
2155 self._generate_backref()
2156 self._join_condition._warn_for_conflicting_sync_targets()
2157 super(RelationshipProperty, self).do_init()
2158 self._lazy_strategy = self._get_strategy((("lazy", "select"),))
2160 def _setup_registry_dependencies(self):
2161 self.parent.mapper.registry._set_depends_on(
2162 self.entity.mapper.registry
2163 )
2165 def _process_dependent_arguments(self):
2166 """Convert incoming configuration arguments to their
2167 proper form.
2169 Callables are resolved, ORM annotations removed.
2171 """
2173 # accept callables for other attributes which may require
2174 # deferred initialization. This technique is used
2175 # by declarative "string configs" and some recipes.
2176 for attr in (
2177 "order_by",
2178 "primaryjoin",
2179 "secondaryjoin",
2180 "secondary",
2181 "_user_defined_foreign_keys",
2182 "remote_side",
2183 ):
2184 attr_value = getattr(self, attr)
2186 if isinstance(attr_value, util.string_types):
2187 setattr(
2188 self,
2189 attr,
2190 self._clsregistry_resolve_arg(
2191 attr_value, favor_tables=attr == "secondary"
2192 )(),
2193 )
2194 elif callable(attr_value) and not _is_mapped_class(attr_value):
2195 setattr(self, attr, attr_value())
2197 # remove "annotations" which are present if mapped class
2198 # descriptors are used to create the join expression.
2199 for attr in "primaryjoin", "secondaryjoin":
2200 val = getattr(self, attr)
2201 if val is not None:
2202 setattr(
2203 self,
2204 attr,
2205 _orm_deannotate(
2206 coercions.expect(
2207 roles.ColumnArgumentRole, val, argname=attr
2208 )
2209 ),
2210 )
2212 if self.secondary is not None and _is_mapped_class(self.secondary):
2213 raise sa_exc.ArgumentError(
2214 "secondary argument %s passed to to relationship() %s must "
2215 "be a Table object or other FROM clause; can't send a mapped "
2216 "class directly as rows in 'secondary' are persisted "
2217 "independently of a class that is mapped "
2218 "to that same table." % (self.secondary, self)
2219 )
2221 # ensure expressions in self.order_by, foreign_keys,
2222 # remote_side are all columns, not strings.
2223 if self.order_by is not False and self.order_by is not None:
2224 self.order_by = tuple(
2225 coercions.expect(
2226 roles.ColumnArgumentRole, x, argname="order_by"
2227 )
2228 for x in util.to_list(self.order_by)
2229 )
2231 self._user_defined_foreign_keys = util.column_set(
2232 coercions.expect(
2233 roles.ColumnArgumentRole, x, argname="foreign_keys"
2234 )
2235 for x in util.to_column_set(self._user_defined_foreign_keys)
2236 )
2238 self.remote_side = util.column_set(
2239 coercions.expect(
2240 roles.ColumnArgumentRole, x, argname="remote_side"
2241 )
2242 for x in util.to_column_set(self.remote_side)
2243 )
2245 self.target = self.entity.persist_selectable
2247 def _setup_join_conditions(self):
2248 self._join_condition = jc = JoinCondition(
2249 parent_persist_selectable=self.parent.persist_selectable,
2250 child_persist_selectable=self.entity.persist_selectable,
2251 parent_local_selectable=self.parent.local_table,
2252 child_local_selectable=self.entity.local_table,
2253 primaryjoin=self.primaryjoin,
2254 secondary=self.secondary,
2255 secondaryjoin=self.secondaryjoin,
2256 parent_equivalents=self.parent._equivalent_columns,
2257 child_equivalents=self.mapper._equivalent_columns,
2258 consider_as_foreign_keys=self._user_defined_foreign_keys,
2259 local_remote_pairs=self.local_remote_pairs,
2260 remote_side=self.remote_side,
2261 self_referential=self._is_self_referential,
2262 prop=self,
2263 support_sync=not self.viewonly,
2264 can_be_synced_fn=self._columns_are_mapped,
2265 )
2266 self.primaryjoin = jc.primaryjoin
2267 self.secondaryjoin = jc.secondaryjoin
2268 self.direction = jc.direction
2269 self.local_remote_pairs = jc.local_remote_pairs
2270 self.remote_side = jc.remote_columns
2271 self.local_columns = jc.local_columns
2272 self.synchronize_pairs = jc.synchronize_pairs
2273 self._calculated_foreign_keys = jc.foreign_key_columns
2274 self.secondary_synchronize_pairs = jc.secondary_synchronize_pairs
2276 @property
2277 def _clsregistry_resolve_arg(self):
2278 return self._clsregistry_resolvers[1]
2280 @property
2281 def _clsregistry_resolve_name(self):
2282 return self._clsregistry_resolvers[0]
2284 @util.memoized_property
2285 @util.preload_module("sqlalchemy.orm.clsregistry")
2286 def _clsregistry_resolvers(self):
2287 _resolver = util.preloaded.orm_clsregistry._resolver
2289 return _resolver(self.parent.class_, self)
2291 @util.preload_module("sqlalchemy.orm.mapper")
2292 def _check_conflicts(self):
2293 """Test that this relationship is legal, warn about
2294 inheritance conflicts."""
2295 mapperlib = util.preloaded.orm_mapper
2296 if self.parent.non_primary and not mapperlib.class_mapper(
2297 self.parent.class_, configure=False
2298 ).has_property(self.key):
2299 raise sa_exc.ArgumentError(
2300 "Attempting to assign a new "
2301 "relationship '%s' to a non-primary mapper on "
2302 "class '%s'. New relationships can only be added "
2303 "to the primary mapper, i.e. the very first mapper "
2304 "created for class '%s' "
2305 % (
2306 self.key,
2307 self.parent.class_.__name__,
2308 self.parent.class_.__name__,
2309 )
2310 )
2312 @property
2313 def cascade(self):
2314 """Return the current cascade setting for this
2315 :class:`.RelationshipProperty`.
2316 """
2317 return self._cascade
2319 @cascade.setter
2320 def cascade(self, cascade):
2321 self._set_cascade(cascade)
2323 def _set_cascade(self, cascade):
2324 cascade = CascadeOptions(cascade)
2326 if self.viewonly:
2327 non_viewonly = set(cascade).difference(
2328 CascadeOptions._viewonly_cascades
2329 )
2330 if non_viewonly:
2331 raise sa_exc.ArgumentError(
2332 'Cascade settings "%s" apply to persistence operations '
2333 "and should not be combined with a viewonly=True "
2334 "relationship." % (", ".join(sorted(non_viewonly)))
2335 )
2337 if "mapper" in self.__dict__:
2338 self._check_cascade_settings(cascade)
2339 self._cascade = cascade
2341 if self._dependency_processor:
2342 self._dependency_processor.cascade = cascade
2344 def _check_cascade_settings(self, cascade):
2345 if (
2346 cascade.delete_orphan
2347 and not self.single_parent
2348 and (self.direction is MANYTOMANY or self.direction is MANYTOONE)
2349 ):
2350 raise sa_exc.ArgumentError(
2351 "For %(direction)s relationship %(rel)s, delete-orphan "
2352 "cascade is normally "
2353 'configured only on the "one" side of a one-to-many '
2354 "relationship, "
2355 'and not on the "many" side of a many-to-one or many-to-many '
2356 "relationship. "
2357 "To force this relationship to allow a particular "
2358 '"%(relatedcls)s" object to be referred towards by only '
2359 'a single "%(clsname)s" object at a time via the '
2360 "%(rel)s relationship, which "
2361 "would allow "
2362 "delete-orphan cascade to take place in this direction, set "
2363 "the single_parent=True flag."
2364 % {
2365 "rel": self,
2366 "direction": "many-to-one"
2367 if self.direction is MANYTOONE
2368 else "many-to-many",
2369 "clsname": self.parent.class_.__name__,
2370 "relatedcls": self.mapper.class_.__name__,
2371 },
2372 code="bbf0",
2373 )
2375 if self.passive_deletes == "all" and (
2376 "delete" in cascade or "delete-orphan" in cascade
2377 ):
2378 raise sa_exc.ArgumentError(
2379 "On %s, can't set passive_deletes='all' in conjunction "
2380 "with 'delete' or 'delete-orphan' cascade" % self
2381 )
2383 if cascade.delete_orphan:
2384 self.mapper.primary_mapper()._delete_orphans.append(
2385 (self.key, self.parent.class_)
2386 )
2388 def _persists_for(self, mapper):
2389 """Return True if this property will persist values on behalf
2390 of the given mapper.
2392 """
2394 return (
2395 self.key in mapper.relationships
2396 and mapper.relationships[self.key] is self
2397 )
2399 def _columns_are_mapped(self, *cols):
2400 """Return True if all columns in the given collection are
2401 mapped by the tables referenced by this :class:`.Relationship`.
2403 """
2404 for c in cols:
2405 if (
2406 self.secondary is not None
2407 and self.secondary.c.contains_column(c)
2408 ):
2409 continue
2410 if not self.parent.persist_selectable.c.contains_column(
2411 c
2412 ) and not self.target.c.contains_column(c):
2413 return False
2414 return True
2416 def _generate_backref(self):
2417 """Interpret the 'backref' instruction to create a
2418 :func:`_orm.relationship` complementary to this one."""
2420 if self.parent.non_primary:
2421 return
2422 if self.backref is not None and not self.back_populates:
2423 if isinstance(self.backref, util.string_types):
2424 backref_key, kwargs = self.backref, {}
2425 else:
2426 backref_key, kwargs = self.backref
2427 mapper = self.mapper.primary_mapper()
2429 if not mapper.concrete:
2430 check = set(mapper.iterate_to_root()).union(
2431 mapper.self_and_descendants
2432 )
2433 for m in check:
2434 if m.has_property(backref_key) and not m.concrete:
2435 raise sa_exc.ArgumentError(
2436 "Error creating backref "
2437 "'%s' on relationship '%s': property of that "
2438 "name exists on mapper '%s'"
2439 % (backref_key, self, m)
2440 )
2442 # determine primaryjoin/secondaryjoin for the
2443 # backref. Use the one we had, so that
2444 # a custom join doesn't have to be specified in
2445 # both directions.
2446 if self.secondary is not None:
2447 # for many to many, just switch primaryjoin/
2448 # secondaryjoin. use the annotated
2449 # pj/sj on the _join_condition.
2450 pj = kwargs.pop(
2451 "primaryjoin",
2452 self._join_condition.secondaryjoin_minus_local,
2453 )
2454 sj = kwargs.pop(
2455 "secondaryjoin",
2456 self._join_condition.primaryjoin_minus_local,
2457 )
2458 else:
2459 pj = kwargs.pop(
2460 "primaryjoin",
2461 self._join_condition.primaryjoin_reverse_remote,
2462 )
2463 sj = kwargs.pop("secondaryjoin", None)
2464 if sj:
2465 raise sa_exc.InvalidRequestError(
2466 "Can't assign 'secondaryjoin' on a backref "
2467 "against a non-secondary relationship."
2468 )
2470 foreign_keys = kwargs.pop(
2471 "foreign_keys", self._user_defined_foreign_keys
2472 )
2473 parent = self.parent.primary_mapper()
2474 kwargs.setdefault("viewonly", self.viewonly)
2475 kwargs.setdefault("post_update", self.post_update)
2476 kwargs.setdefault("passive_updates", self.passive_updates)
2477 kwargs.setdefault("sync_backref", self.sync_backref)
2478 self.back_populates = backref_key
2479 relationship = RelationshipProperty(
2480 parent,
2481 self.secondary,
2482 pj,
2483 sj,
2484 foreign_keys=foreign_keys,
2485 back_populates=self.key,
2486 **kwargs
2487 )
2488 mapper._configure_property(backref_key, relationship)
2490 if self.back_populates:
2491 self._add_reverse_property(self.back_populates)
2493 @util.preload_module("sqlalchemy.orm.dependency")
2494 def _post_init(self):
2495 dependency = util.preloaded.orm_dependency
2497 if self.uselist is None:
2498 self.uselist = self.direction is not MANYTOONE
2499 if not self.viewonly:
2500 self._dependency_processor = (
2501 dependency.DependencyProcessor.from_relationship
2502 )(self)
2504 @util.memoized_property
2505 def _use_get(self):
2506 """memoize the 'use_get' attribute of this RelationshipLoader's
2507 lazyloader."""
2509 strategy = self._lazy_strategy
2510 return strategy.use_get
2512 @util.memoized_property
2513 def _is_self_referential(self):
2514 return self.mapper.common_parent(self.parent)
2516 def _create_joins(
2517 self,
2518 source_polymorphic=False,
2519 source_selectable=None,
2520 dest_selectable=None,
2521 of_type_entity=None,
2522 alias_secondary=False,
2523 extra_criteria=(),
2524 ):
2526 aliased = False
2528 if alias_secondary and self.secondary is not None:
2529 aliased = True
2531 if source_selectable is None:
2532 if source_polymorphic and self.parent.with_polymorphic:
2533 source_selectable = self.parent._with_polymorphic_selectable
2535 if of_type_entity:
2536 dest_mapper = of_type_entity.mapper
2537 if dest_selectable is None:
2538 dest_selectable = of_type_entity.selectable
2539 aliased = True
2540 else:
2541 dest_mapper = self.mapper
2543 if dest_selectable is None:
2544 dest_selectable = self.entity.selectable
2545 if self.mapper.with_polymorphic:
2546 aliased = True
2548 if self._is_self_referential and source_selectable is None:
2549 dest_selectable = dest_selectable._anonymous_fromclause()
2550 aliased = True
2551 elif (
2552 dest_selectable is not self.mapper._with_polymorphic_selectable
2553 or self.mapper.with_polymorphic
2554 ):
2555 aliased = True
2557 single_crit = dest_mapper._single_table_criterion
2558 aliased = aliased or (
2559 source_selectable is not None
2560 and (
2561 source_selectable
2562 is not self.parent._with_polymorphic_selectable
2563 or source_selectable._is_subquery
2564 )
2565 )
2567 (
2568 primaryjoin,
2569 secondaryjoin,
2570 secondary,
2571 target_adapter,
2572 dest_selectable,
2573 ) = self._join_condition.join_targets(
2574 source_selectable,
2575 dest_selectable,
2576 aliased,
2577 single_crit,
2578 extra_criteria,
2579 )
2580 if source_selectable is None:
2581 source_selectable = self.parent.local_table
2582 if dest_selectable is None:
2583 dest_selectable = self.entity.local_table
2584 return (
2585 primaryjoin,
2586 secondaryjoin,
2587 source_selectable,
2588 dest_selectable,
2589 secondary,
2590 target_adapter,
2591 )
2594def _annotate_columns(element, annotations):
2595 def clone(elem):
2596 if isinstance(elem, expression.ColumnClause):
2597 elem = elem._annotate(annotations.copy())
2598 elem._copy_internals(clone=clone)
2599 return elem
2601 if element is not None:
2602 element = clone(element)
2603 clone = None # remove gc cycles
2604 return element
2607class JoinCondition(object):
2608 def __init__(
2609 self,
2610 parent_persist_selectable,
2611 child_persist_selectable,
2612 parent_local_selectable,
2613 child_local_selectable,
2614 primaryjoin=None,
2615 secondary=None,
2616 secondaryjoin=None,
2617 parent_equivalents=None,
2618 child_equivalents=None,
2619 consider_as_foreign_keys=None,
2620 local_remote_pairs=None,
2621 remote_side=None,
2622 self_referential=False,
2623 prop=None,
2624 support_sync=True,
2625 can_be_synced_fn=lambda *c: True,
2626 ):
2627 self.parent_persist_selectable = parent_persist_selectable
2628 self.parent_local_selectable = parent_local_selectable
2629 self.child_persist_selectable = child_persist_selectable
2630 self.child_local_selectable = child_local_selectable
2631 self.parent_equivalents = parent_equivalents
2632 self.child_equivalents = child_equivalents
2633 self.primaryjoin = primaryjoin
2634 self.secondaryjoin = secondaryjoin
2635 self.secondary = secondary
2636 self.consider_as_foreign_keys = consider_as_foreign_keys
2637 self._local_remote_pairs = local_remote_pairs
2638 self._remote_side = remote_side
2639 self.prop = prop
2640 self.self_referential = self_referential
2641 self.support_sync = support_sync
2642 self.can_be_synced_fn = can_be_synced_fn
2643 self._determine_joins()
2644 self._sanitize_joins()
2645 self._annotate_fks()
2646 self._annotate_remote()
2647 self._annotate_local()
2648 self._annotate_parentmapper()
2649 self._setup_pairs()
2650 self._check_foreign_cols(self.primaryjoin, True)
2651 if self.secondaryjoin is not None:
2652 self._check_foreign_cols(self.secondaryjoin, False)
2653 self._determine_direction()
2654 self._check_remote_side()
2655 self._log_joins()
2657 def _log_joins(self):
2658 if self.prop is None:
2659 return
2660 log = self.prop.logger
2661 log.info("%s setup primary join %s", self.prop, self.primaryjoin)
2662 log.info("%s setup secondary join %s", self.prop, self.secondaryjoin)
2663 log.info(
2664 "%s synchronize pairs [%s]",
2665 self.prop,
2666 ",".join(
2667 "(%s => %s)" % (l, r) for (l, r) in self.synchronize_pairs
2668 ),
2669 )
2670 log.info(
2671 "%s secondary synchronize pairs [%s]",
2672 self.prop,
2673 ",".join(
2674 "(%s => %s)" % (l, r)
2675 for (l, r) in self.secondary_synchronize_pairs or []
2676 ),
2677 )
2678 log.info(
2679 "%s local/remote pairs [%s]",
2680 self.prop,
2681 ",".join(
2682 "(%s / %s)" % (l, r) for (l, r) in self.local_remote_pairs
2683 ),
2684 )
2685 log.info(
2686 "%s remote columns [%s]",
2687 self.prop,
2688 ",".join("%s" % col for col in self.remote_columns),
2689 )
2690 log.info(
2691 "%s local columns [%s]",
2692 self.prop,
2693 ",".join("%s" % col for col in self.local_columns),
2694 )
2695 log.info("%s relationship direction %s", self.prop, self.direction)
2697 def _sanitize_joins(self):
2698 """remove the parententity annotation from our join conditions which
2699 can leak in here based on some declarative patterns and maybe others.
2701 We'd want to remove "parentmapper" also, but apparently there's
2702 an exotic use case in _join_fixture_inh_selfref_w_entity
2703 that relies upon it being present, see :ticket:`3364`.
2705 """
2707 self.primaryjoin = _deep_deannotate(
2708 self.primaryjoin, values=("parententity", "proxy_key")
2709 )
2710 if self.secondaryjoin is not None:
2711 self.secondaryjoin = _deep_deannotate(
2712 self.secondaryjoin, values=("parententity", "proxy_key")
2713 )
2715 def _determine_joins(self):
2716 """Determine the 'primaryjoin' and 'secondaryjoin' attributes,
2717 if not passed to the constructor already.
2719 This is based on analysis of the foreign key relationships
2720 between the parent and target mapped selectables.
2722 """
2723 if self.secondaryjoin is not None and self.secondary is None:
2724 raise sa_exc.ArgumentError(
2725 "Property %s specified with secondary "
2726 "join condition but "
2727 "no secondary argument" % self.prop
2728 )
2730 # find a join between the given mapper's mapped table and
2731 # the given table. will try the mapper's local table first
2732 # for more specificity, then if not found will try the more
2733 # general mapped table, which in the case of inheritance is
2734 # a join.
2735 try:
2736 consider_as_foreign_keys = self.consider_as_foreign_keys or None
2737 if self.secondary is not None:
2738 if self.secondaryjoin is None:
2739 self.secondaryjoin = join_condition(
2740 self.child_persist_selectable,
2741 self.secondary,
2742 a_subset=self.child_local_selectable,
2743 consider_as_foreign_keys=consider_as_foreign_keys,
2744 )
2745 if self.primaryjoin is None:
2746 self.primaryjoin = join_condition(
2747 self.parent_persist_selectable,
2748 self.secondary,
2749 a_subset=self.parent_local_selectable,
2750 consider_as_foreign_keys=consider_as_foreign_keys,
2751 )
2752 else:
2753 if self.primaryjoin is None:
2754 self.primaryjoin = join_condition(
2755 self.parent_persist_selectable,
2756 self.child_persist_selectable,
2757 a_subset=self.parent_local_selectable,
2758 consider_as_foreign_keys=consider_as_foreign_keys,
2759 )
2760 except sa_exc.NoForeignKeysError as nfe:
2761 if self.secondary is not None:
2762 util.raise_(
2763 sa_exc.NoForeignKeysError(
2764 "Could not determine join "
2765 "condition between parent/child tables on "
2766 "relationship %s - there are no foreign keys "
2767 "linking these tables via secondary table '%s'. "
2768 "Ensure that referencing columns are associated "
2769 "with a ForeignKey or ForeignKeyConstraint, or "
2770 "specify 'primaryjoin' and 'secondaryjoin' "
2771 "expressions." % (self.prop, self.secondary)
2772 ),
2773 from_=nfe,
2774 )
2775 else:
2776 util.raise_(
2777 sa_exc.NoForeignKeysError(
2778 "Could not determine join "
2779 "condition between parent/child tables on "
2780 "relationship %s - there are no foreign keys "
2781 "linking these tables. "
2782 "Ensure that referencing columns are associated "
2783 "with a ForeignKey or ForeignKeyConstraint, or "
2784 "specify a 'primaryjoin' expression." % self.prop
2785 ),
2786 from_=nfe,
2787 )
2788 except sa_exc.AmbiguousForeignKeysError as afe:
2789 if self.secondary is not None:
2790 util.raise_(
2791 sa_exc.AmbiguousForeignKeysError(
2792 "Could not determine join "
2793 "condition between parent/child tables on "
2794 "relationship %s - there are multiple foreign key "
2795 "paths linking the tables via secondary table '%s'. "
2796 "Specify the 'foreign_keys' "
2797 "argument, providing a list of those columns which "
2798 "should be counted as containing a foreign key "
2799 "reference from the secondary table to each of the "
2800 "parent and child tables."
2801 % (self.prop, self.secondary)
2802 ),
2803 from_=afe,
2804 )
2805 else:
2806 util.raise_(
2807 sa_exc.AmbiguousForeignKeysError(
2808 "Could not determine join "
2809 "condition between parent/child tables on "
2810 "relationship %s - there are multiple foreign key "
2811 "paths linking the tables. Specify the "
2812 "'foreign_keys' argument, providing a list of those "
2813 "columns which should be counted as containing a "
2814 "foreign key reference to the parent table."
2815 % self.prop
2816 ),
2817 from_=afe,
2818 )
2820 @property
2821 def primaryjoin_minus_local(self):
2822 return _deep_deannotate(self.primaryjoin, values=("local", "remote"))
2824 @property
2825 def secondaryjoin_minus_local(self):
2826 return _deep_deannotate(self.secondaryjoin, values=("local", "remote"))
2828 @util.memoized_property
2829 def primaryjoin_reverse_remote(self):
2830 """Return the primaryjoin condition suitable for the
2831 "reverse" direction.
2833 If the primaryjoin was delivered here with pre-existing
2834 "remote" annotations, the local/remote annotations
2835 are reversed. Otherwise, the local/remote annotations
2836 are removed.
2838 """
2839 if self._has_remote_annotations:
2841 def replace(element):
2842 if "remote" in element._annotations:
2843 v = dict(element._annotations)
2844 del v["remote"]
2845 v["local"] = True
2846 return element._with_annotations(v)
2847 elif "local" in element._annotations:
2848 v = dict(element._annotations)
2849 del v["local"]
2850 v["remote"] = True
2851 return element._with_annotations(v)
2853 return visitors.replacement_traverse(self.primaryjoin, {}, replace)
2854 else:
2855 if self._has_foreign_annotations:
2856 # TODO: coverage
2857 return _deep_deannotate(
2858 self.primaryjoin, values=("local", "remote")
2859 )
2860 else:
2861 return _deep_deannotate(self.primaryjoin)
2863 def _has_annotation(self, clause, annotation):
2864 for col in visitors.iterate(clause, {}):
2865 if annotation in col._annotations:
2866 return True
2867 else:
2868 return False
2870 @util.memoized_property
2871 def _has_foreign_annotations(self):
2872 return self._has_annotation(self.primaryjoin, "foreign")
2874 @util.memoized_property
2875 def _has_remote_annotations(self):
2876 return self._has_annotation(self.primaryjoin, "remote")
2878 def _annotate_fks(self):
2879 """Annotate the primaryjoin and secondaryjoin
2880 structures with 'foreign' annotations marking columns
2881 considered as foreign.
2883 """
2884 if self._has_foreign_annotations:
2885 return
2887 if self.consider_as_foreign_keys:
2888 self._annotate_from_fk_list()
2889 else:
2890 self._annotate_present_fks()
2892 def _annotate_from_fk_list(self):
2893 def check_fk(col):
2894 if col in self.consider_as_foreign_keys:
2895 return col._annotate({"foreign": True})
2897 self.primaryjoin = visitors.replacement_traverse(
2898 self.primaryjoin, {}, check_fk
2899 )
2900 if self.secondaryjoin is not None:
2901 self.secondaryjoin = visitors.replacement_traverse(
2902 self.secondaryjoin, {}, check_fk
2903 )
2905 def _annotate_present_fks(self):
2906 if self.secondary is not None:
2907 secondarycols = util.column_set(self.secondary.c)
2908 else:
2909 secondarycols = set()
2911 def is_foreign(a, b):
2912 if isinstance(a, schema.Column) and isinstance(b, schema.Column):
2913 if a.references(b):
2914 return a
2915 elif b.references(a):
2916 return b
2918 if secondarycols:
2919 if a in secondarycols and b not in secondarycols:
2920 return a
2921 elif b in secondarycols and a not in secondarycols:
2922 return b
2924 def visit_binary(binary):
2925 if not isinstance(
2926 binary.left, sql.ColumnElement
2927 ) or not isinstance(binary.right, sql.ColumnElement):
2928 return
2930 if (
2931 "foreign" not in binary.left._annotations
2932 and "foreign" not in binary.right._annotations
2933 ):
2934 col = is_foreign(binary.left, binary.right)
2935 if col is not None:
2936 if col.compare(binary.left):
2937 binary.left = binary.left._annotate({"foreign": True})
2938 elif col.compare(binary.right):
2939 binary.right = binary.right._annotate(
2940 {"foreign": True}
2941 )
2943 self.primaryjoin = visitors.cloned_traverse(
2944 self.primaryjoin, {}, {"binary": visit_binary}
2945 )
2946 if self.secondaryjoin is not None:
2947 self.secondaryjoin = visitors.cloned_traverse(
2948 self.secondaryjoin, {}, {"binary": visit_binary}
2949 )
2951 def _refers_to_parent_table(self):
2952 """Return True if the join condition contains column
2953 comparisons where both columns are in both tables.
2955 """
2956 pt = self.parent_persist_selectable
2957 mt = self.child_persist_selectable
2958 result = [False]
2960 def visit_binary(binary):
2961 c, f = binary.left, binary.right
2962 if (
2963 isinstance(c, expression.ColumnClause)
2964 and isinstance(f, expression.ColumnClause)
2965 and pt.is_derived_from(c.table)
2966 and pt.is_derived_from(f.table)
2967 and mt.is_derived_from(c.table)
2968 and mt.is_derived_from(f.table)
2969 ):
2970 result[0] = True
2972 visitors.traverse(self.primaryjoin, {}, {"binary": visit_binary})
2973 return result[0]
2975 def _tables_overlap(self):
2976 """Return True if parent/child tables have some overlap."""
2978 return selectables_overlap(
2979 self.parent_persist_selectable, self.child_persist_selectable
2980 )
2982 def _annotate_remote(self):
2983 """Annotate the primaryjoin and secondaryjoin
2984 structures with 'remote' annotations marking columns
2985 considered as part of the 'remote' side.
2987 """
2988 if self._has_remote_annotations:
2989 return
2991 if self.secondary is not None:
2992 self._annotate_remote_secondary()
2993 elif self._local_remote_pairs or self._remote_side:
2994 self._annotate_remote_from_args()
2995 elif self._refers_to_parent_table():
2996 self._annotate_selfref(
2997 lambda col: "foreign" in col._annotations, False
2998 )
2999 elif self._tables_overlap():
3000 self._annotate_remote_with_overlap()
3001 else:
3002 self._annotate_remote_distinct_selectables()
3004 def _annotate_remote_secondary(self):
3005 """annotate 'remote' in primaryjoin, secondaryjoin
3006 when 'secondary' is present.
3008 """
3010 def repl(element):
3011 if self.secondary.c.contains_column(element):
3012 return element._annotate({"remote": True})
3014 self.primaryjoin = visitors.replacement_traverse(
3015 self.primaryjoin, {}, repl
3016 )
3017 self.secondaryjoin = visitors.replacement_traverse(
3018 self.secondaryjoin, {}, repl
3019 )
3021 def _annotate_selfref(self, fn, remote_side_given):
3022 """annotate 'remote' in primaryjoin, secondaryjoin
3023 when the relationship is detected as self-referential.
3025 """
3027 def visit_binary(binary):
3028 equated = binary.left.compare(binary.right)
3029 if isinstance(binary.left, expression.ColumnClause) and isinstance(
3030 binary.right, expression.ColumnClause
3031 ):
3032 # assume one to many - FKs are "remote"
3033 if fn(binary.left):
3034 binary.left = binary.left._annotate({"remote": True})
3035 if fn(binary.right) and not equated:
3036 binary.right = binary.right._annotate({"remote": True})
3037 elif not remote_side_given:
3038 self._warn_non_column_elements()
3040 self.primaryjoin = visitors.cloned_traverse(
3041 self.primaryjoin, {}, {"binary": visit_binary}
3042 )
3044 def _annotate_remote_from_args(self):
3045 """annotate 'remote' in primaryjoin, secondaryjoin
3046 when the 'remote_side' or '_local_remote_pairs'
3047 arguments are used.
3049 """
3050 if self._local_remote_pairs:
3051 if self._remote_side:
3052 raise sa_exc.ArgumentError(
3053 "remote_side argument is redundant "
3054 "against more detailed _local_remote_side "
3055 "argument."
3056 )
3058 remote_side = [r for (l, r) in self._local_remote_pairs]
3059 else:
3060 remote_side = self._remote_side
3062 if self._refers_to_parent_table():
3063 self._annotate_selfref(lambda col: col in remote_side, True)
3064 else:
3066 def repl(element):
3067 # use set() to avoid generating ``__eq__()`` expressions
3068 # against each element
3069 if element in set(remote_side):
3070 return element._annotate({"remote": True})
3072 self.primaryjoin = visitors.replacement_traverse(
3073 self.primaryjoin, {}, repl
3074 )
3076 def _annotate_remote_with_overlap(self):
3077 """annotate 'remote' in primaryjoin, secondaryjoin
3078 when the parent/child tables have some set of
3079 tables in common, though is not a fully self-referential
3080 relationship.
3082 """
3084 def visit_binary(binary):
3085 binary.left, binary.right = proc_left_right(
3086 binary.left, binary.right
3087 )
3088 binary.right, binary.left = proc_left_right(
3089 binary.right, binary.left
3090 )
3092 check_entities = (
3093 self.prop is not None and self.prop.mapper is not self.prop.parent
3094 )
3096 def proc_left_right(left, right):
3097 if isinstance(left, expression.ColumnClause) and isinstance(
3098 right, expression.ColumnClause
3099 ):
3100 if self.child_persist_selectable.c.contains_column(
3101 right
3102 ) and self.parent_persist_selectable.c.contains_column(left):
3103 right = right._annotate({"remote": True})
3104 elif (
3105 check_entities
3106 and right._annotations.get("parentmapper") is self.prop.mapper
3107 ):
3108 right = right._annotate({"remote": True})
3109 elif (
3110 check_entities
3111 and left._annotations.get("parentmapper") is self.prop.mapper
3112 ):
3113 left = left._annotate({"remote": True})
3114 else:
3115 self._warn_non_column_elements()
3117 return left, right
3119 self.primaryjoin = visitors.cloned_traverse(
3120 self.primaryjoin, {}, {"binary": visit_binary}
3121 )
3123 def _annotate_remote_distinct_selectables(self):
3124 """annotate 'remote' in primaryjoin, secondaryjoin
3125 when the parent/child tables are entirely
3126 separate.
3128 """
3130 def repl(element):
3131 if self.child_persist_selectable.c.contains_column(element) and (
3132 not self.parent_local_selectable.c.contains_column(element)
3133 or self.child_local_selectable.c.contains_column(element)
3134 ):
3135 return element._annotate({"remote": True})
3137 self.primaryjoin = visitors.replacement_traverse(
3138 self.primaryjoin, {}, repl
3139 )
3141 def _warn_non_column_elements(self):
3142 util.warn(
3143 "Non-simple column elements in primary "
3144 "join condition for property %s - consider using "
3145 "remote() annotations to mark the remote side." % self.prop
3146 )
3148 def _annotate_local(self):
3149 """Annotate the primaryjoin and secondaryjoin
3150 structures with 'local' annotations.
3152 This annotates all column elements found
3153 simultaneously in the parent table
3154 and the join condition that don't have a
3155 'remote' annotation set up from
3156 _annotate_remote() or user-defined.
3158 """
3159 if self._has_annotation(self.primaryjoin, "local"):
3160 return
3162 if self._local_remote_pairs:
3163 local_side = util.column_set(
3164 [l for (l, r) in self._local_remote_pairs]
3165 )
3166 else:
3167 local_side = util.column_set(self.parent_persist_selectable.c)
3169 def locals_(elem):
3170 if "remote" not in elem._annotations and elem in local_side:
3171 return elem._annotate({"local": True})
3173 self.primaryjoin = visitors.replacement_traverse(
3174 self.primaryjoin, {}, locals_
3175 )
3177 def _annotate_parentmapper(self):
3178 if self.prop is None:
3179 return
3181 def parentmappers_(elem):
3182 if "remote" in elem._annotations:
3183 return elem._annotate({"parentmapper": self.prop.mapper})
3184 elif "local" in elem._annotations:
3185 return elem._annotate({"parentmapper": self.prop.parent})
3187 self.primaryjoin = visitors.replacement_traverse(
3188 self.primaryjoin, {}, parentmappers_
3189 )
3191 def _check_remote_side(self):
3192 if not self.local_remote_pairs:
3193 raise sa_exc.ArgumentError(
3194 "Relationship %s could "
3195 "not determine any unambiguous local/remote column "
3196 "pairs based on join condition and remote_side "
3197 "arguments. "
3198 "Consider using the remote() annotation to "
3199 "accurately mark those elements of the join "
3200 "condition that are on the remote side of "
3201 "the relationship." % (self.prop,)
3202 )
3203 else:
3205 not_target = util.column_set(
3206 self.parent_persist_selectable.c
3207 ).difference(self.child_persist_selectable.c)
3209 for _, rmt in self.local_remote_pairs:
3210 if rmt in not_target:
3211 util.warn(
3212 "Expression %s is marked as 'remote', but these "
3213 "column(s) are local to the local side. The "
3214 "remote() annotation is needed only for a "
3215 "self-referential relationship where both sides "
3216 "of the relationship refer to the same tables."
3217 % (rmt,)
3218 )
3220 def _check_foreign_cols(self, join_condition, primary):
3221 """Check the foreign key columns collected and emit error
3222 messages."""
3224 can_sync = False
3226 foreign_cols = self._gather_columns_with_annotation(
3227 join_condition, "foreign"
3228 )
3230 has_foreign = bool(foreign_cols)
3232 if primary:
3233 can_sync = bool(self.synchronize_pairs)
3234 else:
3235 can_sync = bool(self.secondary_synchronize_pairs)
3237 if (
3238 self.support_sync
3239 and can_sync
3240 or (not self.support_sync and has_foreign)
3241 ):
3242 return
3244 # from here below is just determining the best error message
3245 # to report. Check for a join condition using any operator
3246 # (not just ==), perhaps they need to turn on "viewonly=True".
3247 if self.support_sync and has_foreign and not can_sync:
3248 err = (
3249 "Could not locate any simple equality expressions "
3250 "involving locally mapped foreign key columns for "
3251 "%s join condition "
3252 "'%s' on relationship %s."
3253 % (
3254 primary and "primary" or "secondary",
3255 join_condition,
3256 self.prop,
3257 )
3258 )
3259 err += (
3260 " Ensure that referencing columns are associated "
3261 "with a ForeignKey or ForeignKeyConstraint, or are "
3262 "annotated in the join condition with the foreign() "
3263 "annotation. To allow comparison operators other than "
3264 "'==', the relationship can be marked as viewonly=True."
3265 )
3267 raise sa_exc.ArgumentError(err)
3268 else:
3269 err = (
3270 "Could not locate any relevant foreign key columns "
3271 "for %s join condition '%s' on relationship %s."
3272 % (
3273 primary and "primary" or "secondary",
3274 join_condition,
3275 self.prop,
3276 )
3277 )
3278 err += (
3279 " Ensure that referencing columns are associated "
3280 "with a ForeignKey or ForeignKeyConstraint, or are "
3281 "annotated in the join condition with the foreign() "
3282 "annotation."
3283 )
3284 raise sa_exc.ArgumentError(err)
3286 def _determine_direction(self):
3287 """Determine if this relationship is one to many, many to one,
3288 many to many.
3290 """
3291 if self.secondaryjoin is not None:
3292 self.direction = MANYTOMANY
3293 else:
3294 parentcols = util.column_set(self.parent_persist_selectable.c)
3295 targetcols = util.column_set(self.child_persist_selectable.c)
3297 # fk collection which suggests ONETOMANY.
3298 onetomany_fk = targetcols.intersection(self.foreign_key_columns)
3300 # fk collection which suggests MANYTOONE.
3302 manytoone_fk = parentcols.intersection(self.foreign_key_columns)
3304 if onetomany_fk and manytoone_fk:
3305 # fks on both sides. test for overlap of local/remote
3306 # with foreign key.
3307 # we will gather columns directly from their annotations
3308 # without deannotating, so that we can distinguish on a column
3309 # that refers to itself.
3311 # 1. columns that are both remote and FK suggest
3312 # onetomany.
3313 onetomany_local = self._gather_columns_with_annotation(
3314 self.primaryjoin, "remote", "foreign"
3315 )
3317 # 2. columns that are FK but are not remote (e.g. local)
3318 # suggest manytoone.
3319 manytoone_local = set(
3320 [
3321 c
3322 for c in self._gather_columns_with_annotation(
3323 self.primaryjoin, "foreign"
3324 )
3325 if "remote" not in c._annotations
3326 ]
3327 )
3329 # 3. if both collections are present, remove columns that
3330 # refer to themselves. This is for the case of
3331 # and_(Me.id == Me.remote_id, Me.version == Me.version)
3332 if onetomany_local and manytoone_local:
3333 self_equated = self.remote_columns.intersection(
3334 self.local_columns
3335 )
3336 onetomany_local = onetomany_local.difference(self_equated)
3337 manytoone_local = manytoone_local.difference(self_equated)
3339 # at this point, if only one or the other collection is
3340 # present, we know the direction, otherwise it's still
3341 # ambiguous.
3343 if onetomany_local and not manytoone_local:
3344 self.direction = ONETOMANY
3345 elif manytoone_local and not onetomany_local:
3346 self.direction = MANYTOONE
3347 else:
3348 raise sa_exc.ArgumentError(
3349 "Can't determine relationship"
3350 " direction for relationship '%s' - foreign "
3351 "key columns within the join condition are present "
3352 "in both the parent and the child's mapped tables. "
3353 "Ensure that only those columns referring "
3354 "to a parent column are marked as foreign, "
3355 "either via the foreign() annotation or "
3356 "via the foreign_keys argument." % self.prop
3357 )
3358 elif onetomany_fk:
3359 self.direction = ONETOMANY
3360 elif manytoone_fk:
3361 self.direction = MANYTOONE
3362 else:
3363 raise sa_exc.ArgumentError(
3364 "Can't determine relationship "
3365 "direction for relationship '%s' - foreign "
3366 "key columns are present in neither the parent "
3367 "nor the child's mapped tables" % self.prop
3368 )
3370 def _deannotate_pairs(self, collection):
3371 """provide deannotation for the various lists of
3372 pairs, so that using them in hashes doesn't incur
3373 high-overhead __eq__() comparisons against
3374 original columns mapped.
3376 """
3377 return [(x._deannotate(), y._deannotate()) for x, y in collection]
3379 def _setup_pairs(self):
3380 sync_pairs = []
3381 lrp = util.OrderedSet([])
3382 secondary_sync_pairs = []
3384 def go(joincond, collection):
3385 def visit_binary(binary, left, right):
3386 if (
3387 "remote" in right._annotations
3388 and "remote" not in left._annotations
3389 and self.can_be_synced_fn(left)
3390 ):
3391 lrp.add((left, right))
3392 elif (
3393 "remote" in left._annotations
3394 and "remote" not in right._annotations
3395 and self.can_be_synced_fn(right)
3396 ):
3397 lrp.add((right, left))
3398 if binary.operator is operators.eq and self.can_be_synced_fn(
3399 left, right
3400 ):
3401 if "foreign" in right._annotations:
3402 collection.append((left, right))
3403 elif "foreign" in left._annotations:
3404 collection.append((right, left))
3406 visit_binary_product(visit_binary, joincond)
3408 for joincond, collection in [
3409 (self.primaryjoin, sync_pairs),
3410 (self.secondaryjoin, secondary_sync_pairs),
3411 ]:
3412 if joincond is None:
3413 continue
3414 go(joincond, collection)
3416 self.local_remote_pairs = self._deannotate_pairs(lrp)
3417 self.synchronize_pairs = self._deannotate_pairs(sync_pairs)
3418 self.secondary_synchronize_pairs = self._deannotate_pairs(
3419 secondary_sync_pairs
3420 )
3422 _track_overlapping_sync_targets = weakref.WeakKeyDictionary()
3424 def _warn_for_conflicting_sync_targets(self):
3425 if not self.support_sync:
3426 return
3428 # we would like to detect if we are synchronizing any column
3429 # pairs in conflict with another relationship that wishes to sync
3430 # an entirely different column to the same target. This is a
3431 # very rare edge case so we will try to minimize the memory/overhead
3432 # impact of this check
3433 for from_, to_ in [
3434 (from_, to_) for (from_, to_) in self.synchronize_pairs
3435 ] + [
3436 (from_, to_) for (from_, to_) in self.secondary_synchronize_pairs
3437 ]:
3438 # save ourselves a ton of memory and overhead by only
3439 # considering columns that are subject to a overlapping
3440 # FK constraints at the core level. This condition can arise
3441 # if multiple relationships overlap foreign() directly, but
3442 # we're going to assume it's typically a ForeignKeyConstraint-
3443 # level configuration that benefits from this warning.
3445 if to_ not in self._track_overlapping_sync_targets:
3446 self._track_overlapping_sync_targets[
3447 to_
3448 ] = weakref.WeakKeyDictionary({self.prop: from_})
3449 else:
3450 other_props = []
3451 prop_to_from = self._track_overlapping_sync_targets[to_]
3453 for pr, fr_ in prop_to_from.items():
3454 if (
3455 not pr.mapper._dispose_called
3456 and pr not in self.prop._reverse_property
3457 and pr.key not in self.prop._overlaps
3458 and self.prop.key not in pr._overlaps
3459 # note: the "__*" symbol is used internally by
3460 # SQLAlchemy as a general means of suppressing the
3461 # overlaps warning for some extension cases, however
3462 # this is not currently
3463 # a publicly supported symbol and may change at
3464 # any time.
3465 and "__*" not in self.prop._overlaps
3466 and "__*" not in pr._overlaps
3467 and not self.prop.parent.is_sibling(pr.parent)
3468 and not self.prop.mapper.is_sibling(pr.mapper)
3469 and not self.prop.parent.is_sibling(pr.mapper)
3470 and not self.prop.mapper.is_sibling(pr.parent)
3471 and (
3472 self.prop.key != pr.key
3473 or not self.prop.parent.common_parent(pr.parent)
3474 )
3475 ):
3477 other_props.append((pr, fr_))
3479 if other_props:
3480 util.warn(
3481 "relationship '%s' will copy column %s to column %s, "
3482 "which conflicts with relationship(s): %s. "
3483 "If this is not the intention, consider if these "
3484 "relationships should be linked with "
3485 "back_populates, or if viewonly=True should be "
3486 "applied to one or more if they are read-only. "
3487 "For the less common case that foreign key "
3488 "constraints are partially overlapping, the "
3489 "orm.foreign() "
3490 "annotation can be used to isolate the columns that "
3491 "should be written towards. To silence this "
3492 "warning, add the parameter 'overlaps=\"%s\"' to the "
3493 "'%s' relationship."
3494 % (
3495 self.prop,
3496 from_,
3497 to_,
3498 ", ".join(
3499 sorted(
3500 "'%s' (copies %s to %s)" % (pr, fr_, to_)
3501 for (pr, fr_) in other_props
3502 )
3503 ),
3504 ",".join(sorted(pr.key for pr, fr in other_props)),
3505 self.prop,
3506 ),
3507 code="qzyx",
3508 )
3509 self._track_overlapping_sync_targets[to_][self.prop] = from_
3511 @util.memoized_property
3512 def remote_columns(self):
3513 return self._gather_join_annotations("remote")
3515 @util.memoized_property
3516 def local_columns(self):
3517 return self._gather_join_annotations("local")
3519 @util.memoized_property
3520 def foreign_key_columns(self):
3521 return self._gather_join_annotations("foreign")
3523 def _gather_join_annotations(self, annotation):
3524 s = set(
3525 self._gather_columns_with_annotation(self.primaryjoin, annotation)
3526 )
3527 if self.secondaryjoin is not None:
3528 s.update(
3529 self._gather_columns_with_annotation(
3530 self.secondaryjoin, annotation
3531 )
3532 )
3533 return {x._deannotate() for x in s}
3535 def _gather_columns_with_annotation(self, clause, *annotation):
3536 annotation = set(annotation)
3537 return set(
3538 [
3539 col
3540 for col in visitors.iterate(clause, {})
3541 if annotation.issubset(col._annotations)
3542 ]
3543 )
3545 def join_targets(
3546 self,
3547 source_selectable,
3548 dest_selectable,
3549 aliased,
3550 single_crit=None,
3551 extra_criteria=(),
3552 ):
3553 """Given a source and destination selectable, create a
3554 join between them.
3556 This takes into account aliasing the join clause
3557 to reference the appropriate corresponding columns
3558 in the target objects, as well as the extra child
3559 criterion, equivalent column sets, etc.
3561 """
3562 # place a barrier on the destination such that
3563 # replacement traversals won't ever dig into it.
3564 # its internal structure remains fixed
3565 # regardless of context.
3566 dest_selectable = _shallow_annotate(
3567 dest_selectable, {"no_replacement_traverse": True}
3568 )
3570 primaryjoin, secondaryjoin, secondary = (
3571 self.primaryjoin,
3572 self.secondaryjoin,
3573 self.secondary,
3574 )
3576 # adjust the join condition for single table inheritance,
3577 # in the case that the join is to a subclass
3578 # this is analogous to the
3579 # "_adjust_for_single_table_inheritance()" method in Query.
3581 if single_crit is not None:
3582 if secondaryjoin is not None:
3583 secondaryjoin = secondaryjoin & single_crit
3584 else:
3585 primaryjoin = primaryjoin & single_crit
3587 if extra_criteria:
3588 if secondaryjoin is not None:
3589 secondaryjoin = secondaryjoin & sql.and_(*extra_criteria)
3590 else:
3591 primaryjoin = primaryjoin & sql.and_(*extra_criteria)
3593 if aliased:
3594 if secondary is not None:
3595 secondary = secondary._anonymous_fromclause(flat=True)
3596 primary_aliasizer = ClauseAdapter(
3597 secondary, exclude_fn=_ColInAnnotations("local")
3598 )
3599 secondary_aliasizer = ClauseAdapter(
3600 dest_selectable, equivalents=self.child_equivalents
3601 ).chain(primary_aliasizer)
3602 if source_selectable is not None:
3603 primary_aliasizer = ClauseAdapter(
3604 secondary, exclude_fn=_ColInAnnotations("local")
3605 ).chain(
3606 ClauseAdapter(
3607 source_selectable,
3608 equivalents=self.parent_equivalents,
3609 )
3610 )
3612 secondaryjoin = secondary_aliasizer.traverse(secondaryjoin)
3613 else:
3614 primary_aliasizer = ClauseAdapter(
3615 dest_selectable,
3616 exclude_fn=_ColInAnnotations("local"),
3617 equivalents=self.child_equivalents,
3618 )
3619 if source_selectable is not None:
3620 primary_aliasizer.chain(
3621 ClauseAdapter(
3622 source_selectable,
3623 exclude_fn=_ColInAnnotations("remote"),
3624 equivalents=self.parent_equivalents,
3625 )
3626 )
3627 secondary_aliasizer = None
3629 primaryjoin = primary_aliasizer.traverse(primaryjoin)
3630 target_adapter = secondary_aliasizer or primary_aliasizer
3631 target_adapter.exclude_fn = None
3632 else:
3633 target_adapter = None
3634 return (
3635 primaryjoin,
3636 secondaryjoin,
3637 secondary,
3638 target_adapter,
3639 dest_selectable,
3640 )
3642 def create_lazy_clause(self, reverse_direction=False):
3643 binds = util.column_dict()
3644 equated_columns = util.column_dict()
3646 has_secondary = self.secondaryjoin is not None
3648 if has_secondary:
3649 lookup = collections.defaultdict(list)
3650 for l, r in self.local_remote_pairs:
3651 lookup[l].append((l, r))
3652 equated_columns[r] = l
3653 elif not reverse_direction:
3654 for l, r in self.local_remote_pairs:
3655 equated_columns[r] = l
3656 else:
3657 for l, r in self.local_remote_pairs:
3658 equated_columns[l] = r
3660 def col_to_bind(col):
3662 if (
3663 (not reverse_direction and "local" in col._annotations)
3664 or reverse_direction
3665 and (
3666 (has_secondary and col in lookup)
3667 or (not has_secondary and "remote" in col._annotations)
3668 )
3669 ):
3670 if col not in binds:
3671 binds[col] = sql.bindparam(
3672 None, None, type_=col.type, unique=True
3673 )
3674 return binds[col]
3675 return None
3677 lazywhere = self.primaryjoin
3678 if self.secondaryjoin is None or not reverse_direction:
3679 lazywhere = visitors.replacement_traverse(
3680 lazywhere, {}, col_to_bind
3681 )
3683 if self.secondaryjoin is not None:
3684 secondaryjoin = self.secondaryjoin
3685 if reverse_direction:
3686 secondaryjoin = visitors.replacement_traverse(
3687 secondaryjoin, {}, col_to_bind
3688 )
3689 lazywhere = sql.and_(lazywhere, secondaryjoin)
3691 bind_to_col = {binds[col].key: col for col in binds}
3693 return lazywhere, bind_to_col, equated_columns
3696class _ColInAnnotations(object):
3697 """Serializable object that tests for a name in c._annotations."""
3699 __slots__ = ("name",)
3701 def __init__(self, name):
3702 self.name = name
3704 def __call__(self, c):
3705 return self.name in c._annotations