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