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