Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# orm/relationships.py 

2# Copyright (C) 2005-2020 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:`_orm.relationship`. 

10 

11Provides the :class:`.JoinCondition` object, which encapsulates 

12SQL annotation and aliasing behavior focused on the `primaryjoin` 

13and `secondaryjoin` aspects of :func:`_orm.relationship`. 

14 

15""" 

16from __future__ import absolute_import 

17 

18import collections 

19import weakref 

20 

21from . import attributes 

22from . import dependency 

23from . import mapper as mapperlib 

24from .base import state_str 

25from .interfaces import MANYTOMANY 

26from .interfaces import MANYTOONE 

27from .interfaces import ONETOMANY 

28from .interfaces import PropComparator 

29from .interfaces import StrategizedProperty 

30from .util import _orm_annotate 

31from .util import _orm_deannotate 

32from .util import CascadeOptions 

33from .. import exc as sa_exc 

34from .. import log 

35from .. import schema 

36from .. import sql 

37from .. import util 

38from ..inspection import inspect 

39from ..sql import expression 

40from ..sql import operators 

41from ..sql import visitors 

42from ..sql.util import _deep_deannotate 

43from ..sql.util import _shallow_annotate 

44from ..sql.util import adapt_criterion_to_null 

45from ..sql.util import ClauseAdapter 

46from ..sql.util import join_condition 

47from ..sql.util import selectables_overlap 

48from ..sql.util import visit_binary_product 

49 

50 

51def remote(expr): 

52 """Annotate a portion of a primaryjoin expression 

53 with a 'remote' annotation. 

54 

55 See the section :ref:`relationship_custom_foreign` for a 

56 description of use. 

57 

58 .. seealso:: 

59 

60 :ref:`relationship_custom_foreign` 

61 

62 :func:`.foreign` 

63 

64 """ 

65 return _annotate_columns( 

66 expression._clause_element_as_expr(expr), {"remote": True} 

67 ) 

68 

69 

70def foreign(expr): 

71 """Annotate a portion of a primaryjoin expression 

72 with a 'foreign' annotation. 

73 

74 See the section :ref:`relationship_custom_foreign` for a 

75 description of use. 

76 

77 .. seealso:: 

78 

79 :ref:`relationship_custom_foreign` 

80 

81 :func:`.remote` 

82 

83 """ 

84 

85 return _annotate_columns( 

86 expression._clause_element_as_expr(expr), {"foreign": True} 

87 ) 

88 

89 

90@log.class_logger 

91@util.langhelpers.dependency_for("sqlalchemy.orm.properties", add_to_all=True) 

92class RelationshipProperty(StrategizedProperty): 

93 """Describes an object property that holds a single item or list 

94 of items that correspond to a related database table. 

95 

96 Public constructor is the :func:`_orm.relationship` function. 

97 

98 .. seealso:: 

99 

100 :ref:`relationship_config_toplevel` 

101 

102 """ 

103 

104 strategy_wildcard_key = "relationship" 

105 

106 _persistence_only = dict( 

107 passive_deletes=False, 

108 passive_updates=True, 

109 enable_typechecks=True, 

110 active_history=False, 

111 cascade_backrefs=True, 

112 ) 

113 

114 _dependency_processor = None 

115 

116 @util.deprecated_params( 

117 extension=( 

118 "0.7", 

119 ":class:`.AttributeExtension` is deprecated in favor of the " 

120 ":class:`.AttributeEvents` listener interface. The " 

121 ":paramref:`_orm.relationship.extension` parameter will be " 

122 "removed in a future release.", 

123 ) 

124 ) 

125 def __init__( 

126 self, 

127 argument, 

128 secondary=None, 

129 primaryjoin=None, 

130 secondaryjoin=None, 

131 foreign_keys=None, 

132 uselist=None, 

133 order_by=False, 

134 backref=None, 

135 back_populates=None, 

136 post_update=False, 

137 cascade=False, 

138 extension=None, 

139 viewonly=False, 

140 lazy="select", 

141 collection_class=None, 

142 passive_deletes=_persistence_only["passive_deletes"], 

143 passive_updates=_persistence_only["passive_updates"], 

144 remote_side=None, 

145 enable_typechecks=_persistence_only["enable_typechecks"], 

146 join_depth=None, 

147 comparator_factory=None, 

148 single_parent=False, 

149 innerjoin=False, 

150 distinct_target_key=None, 

151 doc=None, 

152 active_history=_persistence_only["active_history"], 

153 cascade_backrefs=_persistence_only["cascade_backrefs"], 

154 load_on_pending=False, 

155 bake_queries=True, 

156 _local_remote_pairs=None, 

157 query_class=None, 

158 info=None, 

159 omit_join=None, 

160 sync_backref=None, 

161 ): 

162 """Provide a relationship between two mapped classes. 

163 

164 This corresponds to a parent-child or associative table relationship. 

165 The constructed class is an instance of 

166 :class:`.RelationshipProperty`. 

167 

168 A typical :func:`_orm.relationship`, used in a classical mapping:: 

169 

170 mapper(Parent, properties={ 

171 'children': relationship(Child) 

172 }) 

173 

174 Some arguments accepted by :func:`_orm.relationship` 

175 optionally accept a 

176 callable function, which when called produces the desired value. 

177 The callable is invoked by the parent :class:`_orm.Mapper` at "mapper 

178 initialization" time, which happens only when mappers are first used, 

179 and is assumed to be after all mappings have been constructed. This 

180 can be used to resolve order-of-declaration and other dependency 

181 issues, such as if ``Child`` is declared below ``Parent`` in the same 

182 file:: 

183 

184 mapper(Parent, properties={ 

185 "children":relationship(lambda: Child, 

186 order_by=lambda: Child.id) 

187 }) 

188 

189 When using the :ref:`declarative_toplevel` extension, the Declarative 

190 initializer allows string arguments to be passed to 

191 :func:`_orm.relationship`. These string arguments are converted into 

192 callables that evaluate the string as Python code, using the 

193 Declarative class-registry as a namespace. This allows the lookup of 

194 related classes to be automatic via their string name, and removes the 

195 need for related classes to be imported into the local module space 

196 before the dependent classes have been declared. It is still required 

197 that the modules in which these related classes appear are imported 

198 anywhere in the application at some point before the related mappings 

199 are actually used, else a lookup error will be raised when the 

200 :func:`_orm.relationship` 

201 attempts to resolve the string reference to the 

202 related class. An example of a string- resolved class is as 

203 follows:: 

204 

205 from sqlalchemy.ext.declarative import declarative_base 

206 

207 Base = declarative_base() 

208 

209 class Parent(Base): 

210 __tablename__ = 'parent' 

211 id = Column(Integer, primary_key=True) 

212 children = relationship("Child", order_by="Child.id") 

213 

214 .. seealso:: 

215 

216 :ref:`relationship_config_toplevel` - Full introductory and 

217 reference documentation for :func:`_orm.relationship`. 

218 

219 :ref:`orm_tutorial_relationship` - ORM tutorial introduction. 

220 

221 :param argument: 

222 A mapped class, or actual :class:`_orm.Mapper` instance, 

223 representing 

224 the target of the relationship. 

225 

226 :paramref:`_orm.relationship.argument` 

227 may also be passed as a callable 

228 function which is evaluated at mapper initialization time, and may 

229 be passed as a string name when using Declarative. 

230 

231 .. warning:: Prior to SQLAlchemy 1.3.16, this value is interpreted 

232 using Python's ``eval()`` function. 

233 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. 

234 See :ref:`declarative_relationship_eval` for details on 

235 declarative evaluation of :func:`_orm.relationship` arguments. 

236 

237 .. versionchanged 1.3.16:: 

238 

239 The string evaluation of the main "argument" no longer accepts an 

240 open ended Python expression, instead only accepting a string 

241 class name or dotted package-qualified name. 

242 

243 .. seealso:: 

244 

245 :ref:`declarative_configuring_relationships` - further detail 

246 on relationship configuration when using Declarative. 

247 

248 :param secondary: 

249 For a many-to-many relationship, specifies the intermediary 

250 table, and is typically an instance of :class:`_schema.Table`. 

251 In less common circumstances, the argument may also be specified 

252 as an :class:`_expression.Alias` construct, or even a 

253 :class:`_expression.Join` construct. 

254 

255 :paramref:`_orm.relationship.secondary` may 

256 also be passed as a callable function which is evaluated at 

257 mapper initialization time. When using Declarative, it may also 

258 be a string argument noting the name of a :class:`_schema.Table` 

259 that is 

260 present in the :class:`_schema.MetaData` 

261 collection associated with the 

262 parent-mapped :class:`_schema.Table`. 

263 

264 .. warning:: When passed as a Python-evaluable string, the 

265 argument is interpreted using Python's ``eval()`` function. 

266 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. 

267 See :ref:`declarative_relationship_eval` for details on 

268 declarative evaluation of :func:`_orm.relationship` arguments. 

269 

270 The :paramref:`_orm.relationship.secondary` keyword argument is 

271 typically applied in the case where the intermediary 

272 :class:`_schema.Table` 

273 is not otherwise expressed in any direct class mapping. If the 

274 "secondary" table is also explicitly mapped elsewhere (e.g. as in 

275 :ref:`association_pattern`), one should consider applying the 

276 :paramref:`_orm.relationship.viewonly` flag so that this 

277 :func:`_orm.relationship` 

278 is not used for persistence operations which 

279 may conflict with those of the association object pattern. 

280 

281 .. seealso:: 

282 

283 :ref:`relationships_many_to_many` - Reference example of "many 

284 to many". 

285 

286 :ref:`orm_tutorial_many_to_many` - ORM tutorial introduction to 

287 many-to-many relationships. 

288 

289 :ref:`self_referential_many_to_many` - Specifics on using 

290 many-to-many in a self-referential case. 

291 

292 :ref:`declarative_many_to_many` - Additional options when using 

293 Declarative. 

294 

295 :ref:`association_pattern` - an alternative to 

296 :paramref:`_orm.relationship.secondary` 

297 when composing association 

298 table relationships, allowing additional attributes to be 

299 specified on the association table. 

300 

301 :ref:`composite_secondary_join` - a lesser-used pattern which 

302 in some cases can enable complex :func:`_orm.relationship` SQL 

303 conditions to be used. 

304 

305 .. versionadded:: 0.9.2 :paramref:`_orm.relationship.secondary` 

306 works 

307 more effectively when referring to a :class:`_expression.Join` 

308 instance. 

309 

310 :param active_history=False: 

311 When ``True``, indicates that the "previous" value for a 

312 many-to-one reference should be loaded when replaced, if 

313 not already loaded. Normally, history tracking logic for 

314 simple many-to-ones only needs to be aware of the "new" 

315 value in order to perform a flush. This flag is available 

316 for applications that make use of 

317 :func:`.attributes.get_history` which also need to know 

318 the "previous" value of the attribute. 

319 

320 :param backref: 

321 Indicates the string name of a property to be placed on the related 

322 mapper's class that will handle this relationship in the other 

323 direction. The other property will be created automatically 

324 when the mappers are configured. Can also be passed as a 

325 :func:`.backref` object to control the configuration of the 

326 new relationship. 

327 

328 .. seealso:: 

329 

330 :ref:`relationships_backref` - Introductory documentation and 

331 examples. 

332 

333 :paramref:`_orm.relationship.back_populates` - alternative form 

334 of backref specification. 

335 

336 :func:`.backref` - allows control over :func:`_orm.relationship` 

337 configuration when using :paramref:`_orm.relationship.backref`. 

338 

339 

340 :param back_populates: 

341 Takes a string name and has the same meaning as 

342 :paramref:`_orm.relationship.backref`, except the complementing 

343 property is **not** created automatically, and instead must be 

344 configured explicitly on the other mapper. The complementing 

345 property should also indicate 

346 :paramref:`_orm.relationship.back_populates` to this relationship to 

347 ensure proper functioning. 

348 

349 .. seealso:: 

350 

351 :ref:`relationships_backref` - Introductory documentation and 

352 examples. 

353 

354 :paramref:`_orm.relationship.backref` - alternative form 

355 of backref specification. 

356 

357 :param bake_queries=True: 

358 Use the :class:`.BakedQuery` cache to cache the construction of SQL 

359 used in lazy loads. True by default. Set to False if the 

360 join condition of the relationship has unusual features that 

361 might not respond well to statement caching. 

362 

363 .. versionchanged:: 1.2 

364 "Baked" loading is the default implementation for the "select", 

365 a.k.a. "lazy" loading strategy for relationships. 

366 

367 .. versionadded:: 1.0.0 

368 

369 .. seealso:: 

370 

371 :ref:`baked_toplevel` 

372 

373 :param cascade: 

374 A comma-separated list of cascade rules which determines how 

375 Session operations should be "cascaded" from parent to child. 

376 This defaults to ``False``, which means the default cascade 

377 should be used - this default cascade is ``"save-update, merge"``. 

378 

379 The available cascades are ``save-update``, ``merge``, 

380 ``expunge``, ``delete``, ``delete-orphan``, and ``refresh-expire``. 

381 An additional option, ``all`` indicates shorthand for 

382 ``"save-update, merge, refresh-expire, 

383 expunge, delete"``, and is often used as in ``"all, delete-orphan"`` 

384 to indicate that related objects should follow along with the 

385 parent object in all cases, and be deleted when de-associated. 

386 

387 .. seealso:: 

388 

389 :ref:`unitofwork_cascades` - Full detail on each of the available 

390 cascade options. 

391 

392 :ref:`tutorial_delete_cascade` - Tutorial example describing 

393 a delete cascade. 

394 

395 :param cascade_backrefs=True: 

396 A boolean value indicating if the ``save-update`` cascade should 

397 operate along an assignment event intercepted by a backref. 

398 When set to ``False``, the attribute managed by this relationship 

399 will not cascade an incoming transient object into the session of a 

400 persistent parent, if the event is received via backref. 

401 

402 .. seealso:: 

403 

404 :ref:`backref_cascade` - Full discussion and examples on how 

405 the :paramref:`_orm.relationship.cascade_backrefs` option is used. 

406 

407 :param collection_class: 

408 A class or callable that returns a new list-holding object. will 

409 be used in place of a plain list for storing elements. 

410 

411 .. seealso:: 

412 

413 :ref:`custom_collections` - Introductory documentation and 

414 examples. 

415 

416 :param comparator_factory: 

417 A class which extends :class:`.RelationshipProperty.Comparator` 

418 which provides custom SQL clause generation for comparison 

419 operations. 

420 

421 .. seealso:: 

422 

423 :class:`.PropComparator` - some detail on redefining comparators 

424 at this level. 

425 

426 :ref:`custom_comparators` - Brief intro to this feature. 

427 

428 

429 :param distinct_target_key=None: 

430 Indicate if a "subquery" eager load should apply the DISTINCT 

431 keyword to the innermost SELECT statement. When left as ``None``, 

432 the DISTINCT keyword will be applied in those cases when the target 

433 columns do not comprise the full primary key of the target table. 

434 When set to ``True``, the DISTINCT keyword is applied to the 

435 innermost SELECT unconditionally. 

436 

437 It may be desirable to set this flag to False when the DISTINCT is 

438 reducing performance of the innermost subquery beyond that of what 

439 duplicate innermost rows may be causing. 

440 

441 .. versionchanged:: 0.9.0 - 

442 :paramref:`_orm.relationship.distinct_target_key` now defaults to 

443 ``None``, so that the feature enables itself automatically for 

444 those cases where the innermost query targets a non-unique 

445 key. 

446 

447 .. seealso:: 

448 

449 :ref:`loading_toplevel` - includes an introduction to subquery 

450 eager loading. 

451 

452 :param doc: 

453 Docstring which will be applied to the resulting descriptor. 

454 

455 :param extension: 

456 an :class:`.AttributeExtension` instance, or list of extensions, 

457 which will be prepended to the list of attribute listeners for 

458 the resulting descriptor placed on the class. 

459 

460 :param foreign_keys: 

461 

462 A list of columns which are to be used as "foreign key" 

463 columns, or columns which refer to the value in a remote 

464 column, within the context of this :func:`_orm.relationship` 

465 object's :paramref:`_orm.relationship.primaryjoin` condition. 

466 That is, if the :paramref:`_orm.relationship.primaryjoin` 

467 condition of this :func:`_orm.relationship` is ``a.id == 

468 b.a_id``, and the values in ``b.a_id`` are required to be 

469 present in ``a.id``, then the "foreign key" column of this 

470 :func:`_orm.relationship` is ``b.a_id``. 

471 

472 In normal cases, the :paramref:`_orm.relationship.foreign_keys` 

473 parameter is **not required.** :func:`_orm.relationship` will 

474 automatically determine which columns in the 

475 :paramref:`_orm.relationship.primaryjoin` condition are to be 

476 considered "foreign key" columns based on those 

477 :class:`_schema.Column` objects that specify 

478 :class:`_schema.ForeignKey`, 

479 or are otherwise listed as referencing columns in a 

480 :class:`_schema.ForeignKeyConstraint` construct. 

481 :paramref:`_orm.relationship.foreign_keys` is only needed when: 

482 

483 1. There is more than one way to construct a join from the local 

484 table to the remote table, as there are multiple foreign key 

485 references present. Setting ``foreign_keys`` will limit the 

486 :func:`_orm.relationship` 

487 to consider just those columns specified 

488 here as "foreign". 

489 

490 2. The :class:`_schema.Table` being mapped does not actually have 

491 :class:`_schema.ForeignKey` or 

492 :class:`_schema.ForeignKeyConstraint` 

493 constructs present, often because the table 

494 was reflected from a database that does not support foreign key 

495 reflection (MySQL MyISAM). 

496 

497 3. The :paramref:`_orm.relationship.primaryjoin` 

498 argument is used to 

499 construct a non-standard join condition, which makes use of 

500 columns or expressions that do not normally refer to their 

501 "parent" column, such as a join condition expressed by a 

502 complex comparison using a SQL function. 

503 

504 The :func:`_orm.relationship` construct will raise informative 

505 error messages that suggest the use of the 

506 :paramref:`_orm.relationship.foreign_keys` parameter when 

507 presented with an ambiguous condition. In typical cases, 

508 if :func:`_orm.relationship` doesn't raise any exceptions, the 

509 :paramref:`_orm.relationship.foreign_keys` parameter is usually 

510 not needed. 

511 

512 :paramref:`_orm.relationship.foreign_keys` may also be passed as a 

513 callable function which is evaluated at mapper initialization time, 

514 and may be passed as a Python-evaluable string when using 

515 Declarative. 

516 

517 .. warning:: When passed as a Python-evaluable string, the 

518 argument is interpreted using Python's ``eval()`` function. 

519 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. 

520 See :ref:`declarative_relationship_eval` for details on 

521 declarative evaluation of :func:`_orm.relationship` arguments. 

522 

523 .. seealso:: 

524 

525 :ref:`relationship_foreign_keys` 

526 

527 :ref:`relationship_custom_foreign` 

528 

529 :func:`.foreign` - allows direct annotation of the "foreign" 

530 columns within a :paramref:`_orm.relationship.primaryjoin` 

531 condition. 

532 

533 :param info: Optional data dictionary which will be populated into the 

534 :attr:`.MapperProperty.info` attribute of this object. 

535 

536 :param innerjoin=False: 

537 When ``True``, joined eager loads will use an inner join to join 

538 against related tables instead of an outer join. The purpose 

539 of this option is generally one of performance, as inner joins 

540 generally perform better than outer joins. 

541 

542 This flag can be set to ``True`` when the relationship references an 

543 object via many-to-one using local foreign keys that are not 

544 nullable, or when the reference is one-to-one or a collection that 

545 is guaranteed to have one or at least one entry. 

546 

547 The option supports the same "nested" and "unnested" options as 

548 that of :paramref:`_orm.joinedload.innerjoin`. See that flag 

549 for details on nested / unnested behaviors. 

550 

551 .. seealso:: 

552 

553 :paramref:`_orm.joinedload.innerjoin` - the option as specified by 

554 loader option, including detail on nesting behavior. 

555 

556 :ref:`what_kind_of_loading` - Discussion of some details of 

557 various loader options. 

558 

559 

560 :param join_depth: 

561 When non-``None``, an integer value indicating how many levels 

562 deep "eager" loaders should join on a self-referring or cyclical 

563 relationship. The number counts how many times the same Mapper 

564 shall be present in the loading condition along a particular join 

565 branch. When left at its default of ``None``, eager loaders 

566 will stop chaining when they encounter a the same target mapper 

567 which is already higher up in the chain. This option applies 

568 both to joined- and subquery- eager loaders. 

569 

570 .. seealso:: 

571 

572 :ref:`self_referential_eager_loading` - Introductory documentation 

573 and examples. 

574 

575 :param lazy='select': specifies 

576 How the related items should be loaded. Default value is 

577 ``select``. Values include: 

578 

579 * ``select`` - items should be loaded lazily when the property is 

580 first accessed, using a separate SELECT statement, or identity map 

581 fetch for simple many-to-one references. 

582 

583 * ``immediate`` - items should be loaded as the parents are loaded, 

584 using a separate SELECT statement, or identity map fetch for 

585 simple many-to-one references. 

586 

587 * ``joined`` - items should be loaded "eagerly" in the same query as 

588 that of the parent, using a JOIN or LEFT OUTER JOIN. Whether 

589 the join is "outer" or not is determined by the 

590 :paramref:`_orm.relationship.innerjoin` parameter. 

591 

592 * ``subquery`` - items should be loaded "eagerly" as the parents are 

593 loaded, using one additional SQL statement, which issues a JOIN to 

594 a subquery of the original statement, for each collection 

595 requested. 

596 

597 * ``selectin`` - items should be loaded "eagerly" as the parents 

598 are loaded, using one or more additional SQL statements, which 

599 issues a JOIN to the immediate parent object, specifying primary 

600 key identifiers using an IN clause. 

601 

602 .. versionadded:: 1.2 

603 

604 * ``noload`` - no loading should occur at any time. This is to 

605 support "write-only" attributes, or attributes which are 

606 populated in some manner specific to the application. 

607 

608 * ``raise`` - lazy loading is disallowed; accessing 

609 the attribute, if its value were not already loaded via eager 

610 loading, will raise an :exc:`~sqlalchemy.exc.InvalidRequestError`. 

611 This strategy can be used when objects are to be detached from 

612 their attached :class:`.Session` after they are loaded. 

613 

614 .. versionadded:: 1.1 

615 

616 * ``raise_on_sql`` - lazy loading that emits SQL is disallowed; 

617 accessing the attribute, if its value were not already loaded via 

618 eager loading, will raise an 

619 :exc:`~sqlalchemy.exc.InvalidRequestError`, **if the lazy load 

620 needs to emit SQL**. If the lazy load can pull the related value 

621 from the identity map or determine that it should be None, the 

622 value is loaded. This strategy can be used when objects will 

623 remain associated with the attached :class:`.Session`, however 

624 additional SELECT statements should be blocked. 

625 

626 .. versionadded:: 1.1 

627 

628 * ``dynamic`` - the attribute will return a pre-configured 

629 :class:`_query.Query` object for all read 

630 operations, onto which further filtering operations can be 

631 applied before iterating the results. See 

632 the section :ref:`dynamic_relationship` for more details. 

633 

634 * True - a synonym for 'select' 

635 

636 * False - a synonym for 'joined' 

637 

638 * None - a synonym for 'noload' 

639 

640 .. seealso:: 

641 

642 :doc:`/orm/loading_relationships` - Full documentation on 

643 relationship loader configuration. 

644 

645 :ref:`dynamic_relationship` - detail on the ``dynamic`` option. 

646 

647 :ref:`collections_noload_raiseload` - notes on "noload" and "raise" 

648 

649 :param load_on_pending=False: 

650 Indicates loading behavior for transient or pending parent objects. 

651 

652 When set to ``True``, causes the lazy-loader to 

653 issue a query for a parent object that is not persistent, meaning it 

654 has never been flushed. This may take effect for a pending object 

655 when autoflush is disabled, or for a transient object that has been 

656 "attached" to a :class:`.Session` but is not part of its pending 

657 collection. 

658 

659 The :paramref:`_orm.relationship.load_on_pending` 

660 flag does not improve 

661 behavior when the ORM is used normally - object references should be 

662 constructed at the object level, not at the foreign key level, so 

663 that they are present in an ordinary way before a flush proceeds. 

664 This flag is not not intended for general use. 

665 

666 .. seealso:: 

667 

668 :meth:`.Session.enable_relationship_loading` - this method 

669 establishes "load on pending" behavior for the whole object, and 

670 also allows loading on objects that remain transient or 

671 detached. 

672 

673 :param order_by: 

674 Indicates the ordering that should be applied when loading these 

675 items. :paramref:`_orm.relationship.order_by` 

676 is expected to refer to 

677 one of the :class:`_schema.Column` 

678 objects to which the target class is 

679 mapped, or the attribute itself bound to the target class which 

680 refers to the column. 

681 

682 :paramref:`_orm.relationship.order_by` 

683 may also be passed as a callable 

684 function which is evaluated at mapper initialization time, and may 

685 be passed as a Python-evaluable string when using Declarative. 

686 

687 .. warning:: When passed as a Python-evaluable string, the 

688 argument is interpreted using Python's ``eval()`` function. 

689 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. 

690 See :ref:`declarative_relationship_eval` for details on 

691 declarative evaluation of :func:`_orm.relationship` arguments. 

692 

693 :param passive_deletes=False: 

694 Indicates loading behavior during delete operations. 

695 

696 A value of True indicates that unloaded child items should not 

697 be loaded during a delete operation on the parent. Normally, 

698 when a parent item is deleted, all child items are loaded so 

699 that they can either be marked as deleted, or have their 

700 foreign key to the parent set to NULL. Marking this flag as 

701 True usually implies an ON DELETE <CASCADE|SET NULL> rule is in 

702 place which will handle updating/deleting child rows on the 

703 database side. 

704 

705 Additionally, setting the flag to the string value 'all' will 

706 disable the "nulling out" of the child foreign keys, when the parent 

707 object is deleted and there is no delete or delete-orphan cascade 

708 enabled. This is typically used when a triggering or error raise 

709 scenario is in place on the database side. Note that the foreign 

710 key attributes on in-session child objects will not be changed after 

711 a flush occurs so this is a very special use-case setting. 

712 Additionally, the "nulling out" will still occur if the child 

713 object is de-associated with the parent. 

714 

715 .. seealso:: 

716 

717 :ref:`passive_deletes` - Introductory documentation 

718 and examples. 

719 

720 :param passive_updates=True: 

721 Indicates the persistence behavior to take when a referenced 

722 primary key value changes in place, indicating that the referencing 

723 foreign key columns will also need their value changed. 

724 

725 When True, it is assumed that ``ON UPDATE CASCADE`` is configured on 

726 the foreign key in the database, and that the database will 

727 handle propagation of an UPDATE from a source column to 

728 dependent rows. When False, the SQLAlchemy 

729 :func:`_orm.relationship` 

730 construct will attempt to emit its own UPDATE statements to 

731 modify related targets. However note that SQLAlchemy **cannot** 

732 emit an UPDATE for more than one level of cascade. Also, 

733 setting this flag to False is not compatible in the case where 

734 the database is in fact enforcing referential integrity, unless 

735 those constraints are explicitly "deferred", if the target backend 

736 supports it. 

737 

738 It is highly advised that an application which is employing 

739 mutable primary keys keeps ``passive_updates`` set to True, 

740 and instead uses the referential integrity features of the database 

741 itself in order to handle the change efficiently and fully. 

742 

743 .. seealso:: 

744 

745 :ref:`passive_updates` - Introductory documentation and 

746 examples. 

747 

748 :paramref:`.mapper.passive_updates` - a similar flag which 

749 takes effect for joined-table inheritance mappings. 

750 

751 :param post_update: 

752 This indicates that the relationship should be handled by a 

753 second UPDATE statement after an INSERT or before a 

754 DELETE. Currently, it also will issue an UPDATE after the 

755 instance was UPDATEd as well, although this technically should 

756 be improved. This flag is used to handle saving bi-directional 

757 dependencies between two individual rows (i.e. each row 

758 references the other), where it would otherwise be impossible to 

759 INSERT or DELETE both rows fully since one row exists before the 

760 other. Use this flag when a particular mapping arrangement will 

761 incur two rows that are dependent on each other, such as a table 

762 that has a one-to-many relationship to a set of child rows, and 

763 also has a column that references a single child row within that 

764 list (i.e. both tables contain a foreign key to each other). If 

765 a flush operation returns an error that a "cyclical 

766 dependency" was detected, this is a cue that you might want to 

767 use :paramref:`_orm.relationship.post_update` to "break" the cycle. 

768 

769 .. seealso:: 

770 

771 :ref:`post_update` - Introductory documentation and examples. 

772 

773 :param primaryjoin: 

774 A SQL expression that will be used as the primary 

775 join of the child object against the parent object, or in a 

776 many-to-many relationship the join of the parent object to the 

777 association table. By default, this value is computed based on the 

778 foreign key relationships of the parent and child tables (or 

779 association table). 

780 

781 :paramref:`_orm.relationship.primaryjoin` may also be passed as a 

782 callable function which is evaluated at mapper initialization time, 

783 and may be passed as a Python-evaluable string when using 

784 Declarative. 

785 

786 .. warning:: When passed as a Python-evaluable string, the 

787 argument is interpreted using Python's ``eval()`` function. 

788 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. 

789 See :ref:`declarative_relationship_eval` for details on 

790 declarative evaluation of :func:`_orm.relationship` arguments. 

791 

792 .. seealso:: 

793 

794 :ref:`relationship_primaryjoin` 

795 

796 :param remote_side: 

797 Used for self-referential relationships, indicates the column or 

798 list of columns that form the "remote side" of the relationship. 

799 

800 :paramref:`_orm.relationship.remote_side` may also be passed as a 

801 callable function which is evaluated at mapper initialization time, 

802 and may be passed as a Python-evaluable string when using 

803 Declarative. 

804 

805 .. warning:: When passed as a Python-evaluable string, the 

806 argument is interpreted using Python's ``eval()`` function. 

807 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. 

808 See :ref:`declarative_relationship_eval` for details on 

809 declarative evaluation of :func:`_orm.relationship` arguments. 

810 

811 .. seealso:: 

812 

813 :ref:`self_referential` - in-depth explanation of how 

814 :paramref:`_orm.relationship.remote_side` 

815 is used to configure self-referential relationships. 

816 

817 :func:`.remote` - an annotation function that accomplishes the 

818 same purpose as :paramref:`_orm.relationship.remote_side`, 

819 typically 

820 when a custom :paramref:`_orm.relationship.primaryjoin` condition 

821 is used. 

822 

823 :param query_class: 

824 A :class:`_query.Query` 

825 subclass that will be used as the base of the 

826 "appender query" returned by a "dynamic" relationship, that 

827 is, a relationship that specifies ``lazy="dynamic"`` or was 

828 otherwise constructed using the :func:`_orm.dynamic_loader` 

829 function. 

830 

831 .. seealso:: 

832 

833 :ref:`dynamic_relationship` - Introduction to "dynamic" 

834 relationship loaders. 

835 

836 :param secondaryjoin: 

837 A SQL expression that will be used as the join of 

838 an association table to the child object. By default, this value is 

839 computed based on the foreign key relationships of the association 

840 and child tables. 

841 

842 :paramref:`_orm.relationship.secondaryjoin` may also be passed as a 

843 callable function which is evaluated at mapper initialization time, 

844 and may be passed as a Python-evaluable string when using 

845 Declarative. 

846 

847 .. warning:: When passed as a Python-evaluable string, the 

848 argument is interpreted using Python's ``eval()`` function. 

849 **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. 

850 See :ref:`declarative_relationship_eval` for details on 

851 declarative evaluation of :func:`_orm.relationship` arguments. 

852 

853 .. seealso:: 

854 

855 :ref:`relationship_primaryjoin` 

856 

857 :param single_parent: 

858 When True, installs a validator which will prevent objects 

859 from being associated with more than one parent at a time. 

860 This is used for many-to-one or many-to-many relationships that 

861 should be treated either as one-to-one or one-to-many. Its usage 

862 is optional, except for :func:`_orm.relationship` constructs which 

863 are many-to-one or many-to-many and also 

864 specify the ``delete-orphan`` cascade option. The 

865 :func:`_orm.relationship` construct itself will raise an error 

866 instructing when this option is required. 

867 

868 .. seealso:: 

869 

870 :ref:`unitofwork_cascades` - includes detail on when the 

871 :paramref:`_orm.relationship.single_parent` 

872 flag may be appropriate. 

873 

874 :param uselist: 

875 A boolean that indicates if this property should be loaded as a 

876 list or a scalar. In most cases, this value is determined 

877 automatically by :func:`_orm.relationship` at mapper configuration 

878 time, based on the type and direction 

879 of the relationship - one to many forms a list, many to one 

880 forms a scalar, many to many is a list. If a scalar is desired 

881 where normally a list would be present, such as a bi-directional 

882 one-to-one relationship, set :paramref:`_orm.relationship.uselist` 

883 to 

884 False. 

885 

886 The :paramref:`_orm.relationship.uselist` 

887 flag is also available on an 

888 existing :func:`_orm.relationship` 

889 construct as a read-only attribute, 

890 which can be used to determine if this :func:`_orm.relationship` 

891 deals 

892 with collections or scalar attributes:: 

893 

894 >>> User.addresses.property.uselist 

895 True 

896 

897 .. seealso:: 

898 

899 :ref:`relationships_one_to_one` - Introduction to the "one to 

900 one" relationship pattern, which is typically when the 

901 :paramref:`_orm.relationship.uselist` flag is needed. 

902 

903 :param viewonly=False: 

904 When set to ``True``, the relationship is used only for loading 

905 objects, and not for any persistence operation. A 

906 :func:`_orm.relationship` which specifies 

907 :paramref:`_orm.relationship.viewonly` can work 

908 with a wider range of SQL operations within the 

909 :paramref:`_orm.relationship.primaryjoin` condition, including 

910 operations that feature the use of a variety of comparison operators 

911 as well as SQL functions such as :func:`_expression.cast`. The 

912 :paramref:`_orm.relationship.viewonly` 

913 flag is also of general use when defining any kind of 

914 :func:`_orm.relationship` that doesn't represent 

915 the full set of related objects, to prevent modifications of the 

916 collection from resulting in persistence operations. 

917 

918 When using the :paramref:`_orm.relationship.viewonly` flag in 

919 conjunction with backrefs, the 

920 :paramref:`_orm.relationship.sync_backref` should be set to False; 

921 this indicates that the backref should not actually populate this 

922 relationship with data when changes occur on the other side; as this 

923 is a viewonly relationship, it cannot accommodate changes in state 

924 correctly as these will not be persisted. 

925 

926 .. versionadded:: 1.3.17 - the 

927 :paramref:`_orm.relationship.sync_backref` 

928 flag set to False is required when using viewonly in conjunction 

929 with backrefs. A warning is emitted when this flag is not set. 

930 

931 .. seealso:: 

932 

933 :paramref:`_orm.relationship.sync_backref` 

934 

935 :param sync_backref: 

936 A boolean that enables the events used to synchronize the in-Python 

937 attributes when this relationship is target of either 

938 :paramref:`_orm.relationship.backref` or 

939 :paramref:`_orm.relationship.back_populates`. 

940 

941 Defaults to ``None``, which indicates that an automatic value should 

942 be selected based on the value of the 

943 :paramref:`_orm.relationship.viewonly` flag. When left at its 

944 default, changes in state for writable relationships will be 

945 back-populated normally. For viewonly relationships, a warning is 

946 emitted unless the flag is set to ``False``. 

947 

948 .. versionadded:: 1.3.17 

949 

950 .. seealso:: 

951 

952 :paramref:`_orm.relationship.viewonly` 

953 

954 :param omit_join: 

955 Allows manual control over the "selectin" automatic join 

956 optimization. Set to ``False`` to disable the "omit join" feature 

957 added in SQLAlchemy 1.3; or leave as ``None`` to leave automatic 

958 optimization in place. 

959 

960 .. note:: This flag may only be set to ``False``. It is not 

961 necessary to set it to ``True`` as the "omit_join" optimization is 

962 automatically detected; if it is not detected, then the 

963 optimization is not supported. 

964 

965 .. versionchanged:: 1.3.11 setting ``omit_join`` to True will now 

966 emit a warning as this was not the intended use of this flag. 

967 

968 .. versionadded:: 1.3 

969 

970 

971 """ 

972 super(RelationshipProperty, self).__init__() 

973 

974 self.uselist = uselist 

975 self.argument = argument 

976 self.secondary = secondary 

977 self.primaryjoin = primaryjoin 

978 self.secondaryjoin = secondaryjoin 

979 self.post_update = post_update 

980 self.direction = None 

981 self.viewonly = viewonly 

982 if viewonly: 

983 self._warn_for_persistence_only_flags( 

984 passive_deletes=passive_deletes, 

985 passive_updates=passive_updates, 

986 enable_typechecks=enable_typechecks, 

987 active_history=active_history, 

988 cascade_backrefs=cascade_backrefs, 

989 ) 

990 if viewonly and sync_backref: 

991 raise sa_exc.ArgumentError( 

992 "sync_backref and viewonly cannot both be True" 

993 ) 

994 self.sync_backref = sync_backref 

995 self.lazy = lazy 

996 self.single_parent = single_parent 

997 self._user_defined_foreign_keys = foreign_keys 

998 self.collection_class = collection_class 

999 self.passive_deletes = passive_deletes 

1000 self.cascade_backrefs = cascade_backrefs 

1001 self.passive_updates = passive_updates 

1002 self.remote_side = remote_side 

1003 self.enable_typechecks = enable_typechecks 

1004 self.query_class = query_class 

1005 self.innerjoin = innerjoin 

1006 self.distinct_target_key = distinct_target_key 

1007 self.doc = doc 

1008 self.active_history = active_history 

1009 self.join_depth = join_depth 

1010 if omit_join: 

1011 util.warn( 

1012 "setting omit_join to True is not supported; selectin " 

1013 "loading of this relationship may not work correctly if this " 

1014 "flag is set explicitly. omit_join optimization is " 

1015 "automatically detected for conditions under which it is " 

1016 "supported." 

1017 ) 

1018 

1019 self.omit_join = omit_join 

1020 self.local_remote_pairs = _local_remote_pairs 

1021 self.extension = extension 

1022 self.bake_queries = bake_queries 

1023 self.load_on_pending = load_on_pending 

1024 self.comparator_factory = ( 

1025 comparator_factory or RelationshipProperty.Comparator 

1026 ) 

1027 self.comparator = self.comparator_factory(self, None) 

1028 util.set_creation_order(self) 

1029 

1030 if info is not None: 

1031 self.info = info 

1032 

1033 self.strategy_key = (("lazy", self.lazy),) 

1034 

1035 self._reverse_property = set() 

1036 

1037 if cascade is not False: 

1038 self.cascade = cascade 

1039 else: 

1040 self._set_cascade("save-update, merge", warn=False) 

1041 

1042 self.order_by = order_by 

1043 

1044 self.back_populates = back_populates 

1045 

1046 if self.back_populates: 

1047 if backref: 

1048 raise sa_exc.ArgumentError( 

1049 "backref and back_populates keyword arguments " 

1050 "are mutually exclusive" 

1051 ) 

1052 self.backref = None 

1053 else: 

1054 self.backref = backref 

1055 

1056 def _warn_for_persistence_only_flags(self, **kw): 

1057 for k, v in kw.items(): 

1058 if v != self._persistence_only[k]: 

1059 # we are warning here rather than warn deprecated as this is a 

1060 # configuration mistake, and Python shows regular warnings more 

1061 # aggressively than deprecation warnings by default. Unlike the 

1062 # case of setting viewonly with cascade, the settings being 

1063 # warned about here are not actively doing the wrong thing 

1064 # against viewonly=True, so it is not as urgent to have these 

1065 # raise an error. 

1066 util.warn( 

1067 "Setting %s on relationship() while also " 

1068 "setting viewonly=True does not make sense, as a " 

1069 "viewonly=True relationship does not perform persistence " 

1070 "operations. This configuration may raise an error " 

1071 "in a future release." % (k,) 

1072 ) 

1073 

1074 def instrument_class(self, mapper): 

1075 attributes.register_descriptor( 

1076 mapper.class_, 

1077 self.key, 

1078 comparator=self.comparator_factory(self, mapper), 

1079 parententity=mapper, 

1080 doc=self.doc, 

1081 ) 

1082 

1083 class Comparator(PropComparator): 

1084 """Produce boolean, comparison, and other operators for 

1085 :class:`.RelationshipProperty` attributes. 

1086 

1087 See the documentation for :class:`.PropComparator` for a brief 

1088 overview of ORM level operator definition. 

1089 

1090 .. seealso:: 

1091 

1092 :class:`.PropComparator` 

1093 

1094 :class:`.ColumnProperty.Comparator` 

1095 

1096 :class:`.ColumnOperators` 

1097 

1098 :ref:`types_operators` 

1099 

1100 :attr:`.TypeEngine.comparator_factory` 

1101 

1102 """ 

1103 

1104 _of_type = None 

1105 

1106 def __init__( 

1107 self, prop, parentmapper, adapt_to_entity=None, of_type=None 

1108 ): 

1109 """Construction of :class:`.RelationshipProperty.Comparator` 

1110 is internal to the ORM's attribute mechanics. 

1111 

1112 """ 

1113 self.prop = prop 

1114 self._parententity = parentmapper 

1115 self._adapt_to_entity = adapt_to_entity 

1116 if of_type: 

1117 self._of_type = of_type 

1118 

1119 def adapt_to_entity(self, adapt_to_entity): 

1120 return self.__class__( 

1121 self.property, 

1122 self._parententity, 

1123 adapt_to_entity=adapt_to_entity, 

1124 of_type=self._of_type, 

1125 ) 

1126 

1127 @util.memoized_property 

1128 def entity(self): 

1129 """The target entity referred to by this 

1130 :class:`.RelationshipProperty.Comparator`. 

1131 

1132 This is either a :class:`_orm.Mapper` or :class:`.AliasedInsp` 

1133 object. 

1134 

1135 This is the "target" or "remote" side of the 

1136 :func:`_orm.relationship`. 

1137 

1138 """ 

1139 return self.property.entity 

1140 

1141 @util.memoized_property 

1142 def mapper(self): 

1143 """The target :class:`_orm.Mapper` referred to by this 

1144 :class:`.RelationshipProperty.Comparator`. 

1145 

1146 This is the "target" or "remote" side of the 

1147 :func:`_orm.relationship`. 

1148 

1149 """ 

1150 return self.property.mapper 

1151 

1152 @util.memoized_property 

1153 def _parententity(self): 

1154 return self.property.parent 

1155 

1156 def _source_selectable(self): 

1157 if self._adapt_to_entity: 

1158 return self._adapt_to_entity.selectable 

1159 else: 

1160 return self.property.parent._with_polymorphic_selectable 

1161 

1162 def __clause_element__(self): 

1163 adapt_from = self._source_selectable() 

1164 if self._of_type: 

1165 of_type_mapper = inspect(self._of_type).mapper 

1166 else: 

1167 of_type_mapper = None 

1168 

1169 ( 

1170 pj, 

1171 sj, 

1172 source, 

1173 dest, 

1174 secondary, 

1175 target_adapter, 

1176 ) = self.property._create_joins( 

1177 source_selectable=adapt_from, 

1178 source_polymorphic=True, 

1179 of_type_mapper=of_type_mapper, 

1180 alias_secondary=True, 

1181 ) 

1182 if sj is not None: 

1183 return pj & sj 

1184 else: 

1185 return pj 

1186 

1187 def of_type(self, cls): 

1188 r"""Redefine this object in terms of a polymorphic subclass. 

1189 

1190 See :meth:`.PropComparator.of_type` for an example. 

1191 

1192 """ 

1193 return RelationshipProperty.Comparator( 

1194 self.property, 

1195 self._parententity, 

1196 adapt_to_entity=self._adapt_to_entity, 

1197 of_type=cls, 

1198 ) 

1199 

1200 def in_(self, other): 

1201 """Produce an IN clause - this is not implemented 

1202 for :func:`_orm.relationship`-based attributes at this time. 

1203 

1204 """ 

1205 raise NotImplementedError( 

1206 "in_() not yet supported for " 

1207 "relationships. For a simple " 

1208 "many-to-one, use in_() against " 

1209 "the set of foreign key values." 

1210 ) 

1211 

1212 __hash__ = None 

1213 

1214 def __eq__(self, other): 

1215 """Implement the ``==`` operator. 

1216 

1217 In a many-to-one context, such as:: 

1218 

1219 MyClass.some_prop == <some object> 

1220 

1221 this will typically produce a 

1222 clause such as:: 

1223 

1224 mytable.related_id == <some id> 

1225 

1226 Where ``<some id>`` is the primary key of the given 

1227 object. 

1228 

1229 The ``==`` operator provides partial functionality for non- 

1230 many-to-one comparisons: 

1231 

1232 * Comparisons against collections are not supported. 

1233 Use :meth:`~.RelationshipProperty.Comparator.contains`. 

1234 * Compared to a scalar one-to-many, will produce a 

1235 clause that compares the target columns in the parent to 

1236 the given target. 

1237 * Compared to a scalar many-to-many, an alias 

1238 of the association table will be rendered as 

1239 well, forming a natural join that is part of the 

1240 main body of the query. This will not work for 

1241 queries that go beyond simple AND conjunctions of 

1242 comparisons, such as those which use OR. Use 

1243 explicit joins, outerjoins, or 

1244 :meth:`~.RelationshipProperty.Comparator.has` for 

1245 more comprehensive non-many-to-one scalar 

1246 membership tests. 

1247 * Comparisons against ``None`` given in a one-to-many 

1248 or many-to-many context produce a NOT EXISTS clause. 

1249 

1250 """ 

1251 if isinstance(other, (util.NoneType, expression.Null)): 

1252 if self.property.direction in [ONETOMANY, MANYTOMANY]: 

1253 return ~self._criterion_exists() 

1254 else: 

1255 return _orm_annotate( 

1256 self.property._optimized_compare( 

1257 None, adapt_source=self.adapter 

1258 ) 

1259 ) 

1260 elif self.property.uselist: 

1261 raise sa_exc.InvalidRequestError( 

1262 "Can't compare a collection to an object or collection; " 

1263 "use contains() to test for membership." 

1264 ) 

1265 else: 

1266 return _orm_annotate( 

1267 self.property._optimized_compare( 

1268 other, adapt_source=self.adapter 

1269 ) 

1270 ) 

1271 

1272 def _criterion_exists(self, criterion=None, **kwargs): 

1273 if getattr(self, "_of_type", None): 

1274 info = inspect(self._of_type) 

1275 target_mapper, to_selectable, is_aliased_class = ( 

1276 info.mapper, 

1277 info.selectable, 

1278 info.is_aliased_class, 

1279 ) 

1280 if self.property._is_self_referential and not is_aliased_class: 

1281 to_selectable = to_selectable.alias() 

1282 

1283 single_crit = target_mapper._single_table_criterion 

1284 if single_crit is not None: 

1285 if criterion is not None: 

1286 criterion = single_crit & criterion 

1287 else: 

1288 criterion = single_crit 

1289 else: 

1290 is_aliased_class = False 

1291 to_selectable = None 

1292 

1293 if self.adapter: 

1294 source_selectable = self._source_selectable() 

1295 else: 

1296 source_selectable = None 

1297 

1298 ( 

1299 pj, 

1300 sj, 

1301 source, 

1302 dest, 

1303 secondary, 

1304 target_adapter, 

1305 ) = self.property._create_joins( 

1306 dest_polymorphic=True, 

1307 dest_selectable=to_selectable, 

1308 source_selectable=source_selectable, 

1309 ) 

1310 

1311 for k in kwargs: 

1312 crit = getattr(self.property.mapper.class_, k) == kwargs[k] 

1313 if criterion is None: 

1314 criterion = crit 

1315 else: 

1316 criterion = criterion & crit 

1317 

1318 # annotate the *local* side of the join condition, in the case 

1319 # of pj + sj this is the full primaryjoin, in the case of just 

1320 # pj its the local side of the primaryjoin. 

1321 if sj is not None: 

1322 j = _orm_annotate(pj) & sj 

1323 else: 

1324 j = _orm_annotate(pj, exclude=self.property.remote_side) 

1325 

1326 if ( 

1327 criterion is not None 

1328 and target_adapter 

1329 and not is_aliased_class 

1330 ): 

1331 # limit this adapter to annotated only? 

1332 criterion = target_adapter.traverse(criterion) 

1333 

1334 # only have the "joined left side" of what we 

1335 # return be subject to Query adaption. The right 

1336 # side of it is used for an exists() subquery and 

1337 # should not correlate or otherwise reach out 

1338 # to anything in the enclosing query. 

1339 if criterion is not None: 

1340 criterion = criterion._annotate( 

1341 {"no_replacement_traverse": True} 

1342 ) 

1343 

1344 crit = j & sql.True_._ifnone(criterion) 

1345 

1346 if secondary is not None: 

1347 ex = sql.exists( 

1348 [1], crit, from_obj=[dest, secondary] 

1349 ).correlate_except(dest, secondary) 

1350 else: 

1351 ex = sql.exists([1], crit, from_obj=dest).correlate_except( 

1352 dest 

1353 ) 

1354 return ex 

1355 

1356 def any(self, criterion=None, **kwargs): 

1357 """Produce an expression that tests a collection against 

1358 particular criterion, using EXISTS. 

1359 

1360 An expression like:: 

1361 

1362 session.query(MyClass).filter( 

1363 MyClass.somereference.any(SomeRelated.x==2) 

1364 ) 

1365 

1366 

1367 Will produce a query like:: 

1368 

1369 SELECT * FROM my_table WHERE 

1370 EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id 

1371 AND related.x=2) 

1372 

1373 Because :meth:`~.RelationshipProperty.Comparator.any` uses 

1374 a correlated subquery, its performance is not nearly as 

1375 good when compared against large target tables as that of 

1376 using a join. 

1377 

1378 :meth:`~.RelationshipProperty.Comparator.any` is particularly 

1379 useful for testing for empty collections:: 

1380 

1381 session.query(MyClass).filter( 

1382 ~MyClass.somereference.any() 

1383 ) 

1384 

1385 will produce:: 

1386 

1387 SELECT * FROM my_table WHERE 

1388 NOT EXISTS (SELECT 1 FROM related WHERE 

1389 related.my_id=my_table.id) 

1390 

1391 :meth:`~.RelationshipProperty.Comparator.any` is only 

1392 valid for collections, i.e. a :func:`_orm.relationship` 

1393 that has ``uselist=True``. For scalar references, 

1394 use :meth:`~.RelationshipProperty.Comparator.has`. 

1395 

1396 """ 

1397 if not self.property.uselist: 

1398 raise sa_exc.InvalidRequestError( 

1399 "'any()' not implemented for scalar " 

1400 "attributes. Use has()." 

1401 ) 

1402 

1403 return self._criterion_exists(criterion, **kwargs) 

1404 

1405 def has(self, criterion=None, **kwargs): 

1406 """Produce an expression that tests a scalar reference against 

1407 particular criterion, using EXISTS. 

1408 

1409 An expression like:: 

1410 

1411 session.query(MyClass).filter( 

1412 MyClass.somereference.has(SomeRelated.x==2) 

1413 ) 

1414 

1415 

1416 Will produce a query like:: 

1417 

1418 SELECT * FROM my_table WHERE 

1419 EXISTS (SELECT 1 FROM related WHERE 

1420 related.id==my_table.related_id AND related.x=2) 

1421 

1422 Because :meth:`~.RelationshipProperty.Comparator.has` uses 

1423 a correlated subquery, its performance is not nearly as 

1424 good when compared against large target tables as that of 

1425 using a join. 

1426 

1427 :meth:`~.RelationshipProperty.Comparator.has` is only 

1428 valid for scalar references, i.e. a :func:`_orm.relationship` 

1429 that has ``uselist=False``. For collection references, 

1430 use :meth:`~.RelationshipProperty.Comparator.any`. 

1431 

1432 """ 

1433 if self.property.uselist: 

1434 raise sa_exc.InvalidRequestError( 

1435 "'has()' not implemented for collections. " "Use any()." 

1436 ) 

1437 return self._criterion_exists(criterion, **kwargs) 

1438 

1439 def contains(self, other, **kwargs): 

1440 """Return a simple expression that tests a collection for 

1441 containment of a particular item. 

1442 

1443 :meth:`~.RelationshipProperty.Comparator.contains` is 

1444 only valid for a collection, i.e. a 

1445 :func:`_orm.relationship` that implements 

1446 one-to-many or many-to-many with ``uselist=True``. 

1447 

1448 When used in a simple one-to-many context, an 

1449 expression like:: 

1450 

1451 MyClass.contains(other) 

1452 

1453 Produces a clause like:: 

1454 

1455 mytable.id == <some id> 

1456 

1457 Where ``<some id>`` is the value of the foreign key 

1458 attribute on ``other`` which refers to the primary 

1459 key of its parent object. From this it follows that 

1460 :meth:`~.RelationshipProperty.Comparator.contains` is 

1461 very useful when used with simple one-to-many 

1462 operations. 

1463 

1464 For many-to-many operations, the behavior of 

1465 :meth:`~.RelationshipProperty.Comparator.contains` 

1466 has more caveats. The association table will be 

1467 rendered in the statement, producing an "implicit" 

1468 join, that is, includes multiple tables in the FROM 

1469 clause which are equated in the WHERE clause:: 

1470 

1471 query(MyClass).filter(MyClass.contains(other)) 

1472 

1473 Produces a query like:: 

1474 

1475 SELECT * FROM my_table, my_association_table AS 

1476 my_association_table_1 WHERE 

1477 my_table.id = my_association_table_1.parent_id 

1478 AND my_association_table_1.child_id = <some id> 

1479 

1480 Where ``<some id>`` would be the primary key of 

1481 ``other``. From the above, it is clear that 

1482 :meth:`~.RelationshipProperty.Comparator.contains` 

1483 will **not** work with many-to-many collections when 

1484 used in queries that move beyond simple AND 

1485 conjunctions, such as multiple 

1486 :meth:`~.RelationshipProperty.Comparator.contains` 

1487 expressions joined by OR. In such cases subqueries or 

1488 explicit "outer joins" will need to be used instead. 

1489 See :meth:`~.RelationshipProperty.Comparator.any` for 

1490 a less-performant alternative using EXISTS, or refer 

1491 to :meth:`_query.Query.outerjoin` 

1492 as well as :ref:`ormtutorial_joins` 

1493 for more details on constructing outer joins. 

1494 

1495 """ 

1496 if not self.property.uselist: 

1497 raise sa_exc.InvalidRequestError( 

1498 "'contains' not implemented for scalar " 

1499 "attributes. Use ==" 

1500 ) 

1501 clause = self.property._optimized_compare( 

1502 other, adapt_source=self.adapter 

1503 ) 

1504 

1505 if self.property.secondaryjoin is not None: 

1506 clause.negation_clause = self.__negated_contains_or_equals( 

1507 other 

1508 ) 

1509 

1510 return clause 

1511 

1512 def __negated_contains_or_equals(self, other): 

1513 if self.property.direction == MANYTOONE: 

1514 state = attributes.instance_state(other) 

1515 

1516 def state_bindparam(x, state, col): 

1517 dict_ = state.dict 

1518 return sql.bindparam( 

1519 x, 

1520 unique=True, 

1521 callable_=self.property._get_attr_w_warn_on_none( 

1522 self.property.mapper, state, dict_, col 

1523 ), 

1524 ) 

1525 

1526 def adapt(col): 

1527 if self.adapter: 

1528 return self.adapter(col) 

1529 else: 

1530 return col 

1531 

1532 if self.property._use_get: 

1533 return sql.and_( 

1534 *[ 

1535 sql.or_( 

1536 adapt(x) 

1537 != state_bindparam(adapt(x), state, y), 

1538 adapt(x) == None, 

1539 ) 

1540 for (x, y) in self.property.local_remote_pairs 

1541 ] 

1542 ) 

1543 

1544 criterion = sql.and_( 

1545 *[ 

1546 x == y 

1547 for (x, y) in zip( 

1548 self.property.mapper.primary_key, 

1549 self.property.mapper.primary_key_from_instance(other), 

1550 ) 

1551 ] 

1552 ) 

1553 

1554 return ~self._criterion_exists(criterion) 

1555 

1556 def __ne__(self, other): 

1557 """Implement the ``!=`` operator. 

1558 

1559 In a many-to-one context, such as:: 

1560 

1561 MyClass.some_prop != <some object> 

1562 

1563 This will typically produce a clause such as:: 

1564 

1565 mytable.related_id != <some id> 

1566 

1567 Where ``<some id>`` is the primary key of the 

1568 given object. 

1569 

1570 The ``!=`` operator provides partial functionality for non- 

1571 many-to-one comparisons: 

1572 

1573 * Comparisons against collections are not supported. 

1574 Use 

1575 :meth:`~.RelationshipProperty.Comparator.contains` 

1576 in conjunction with :func:`_expression.not_`. 

1577 * Compared to a scalar one-to-many, will produce a 

1578 clause that compares the target columns in the parent to 

1579 the given target. 

1580 * Compared to a scalar many-to-many, an alias 

1581 of the association table will be rendered as 

1582 well, forming a natural join that is part of the 

1583 main body of the query. This will not work for 

1584 queries that go beyond simple AND conjunctions of 

1585 comparisons, such as those which use OR. Use 

1586 explicit joins, outerjoins, or 

1587 :meth:`~.RelationshipProperty.Comparator.has` in 

1588 conjunction with :func:`_expression.not_` for 

1589 more comprehensive non-many-to-one scalar 

1590 membership tests. 

1591 * Comparisons against ``None`` given in a one-to-many 

1592 or many-to-many context produce an EXISTS clause. 

1593 

1594 """ 

1595 if isinstance(other, (util.NoneType, expression.Null)): 

1596 if self.property.direction == MANYTOONE: 

1597 return _orm_annotate( 

1598 ~self.property._optimized_compare( 

1599 None, adapt_source=self.adapter 

1600 ) 

1601 ) 

1602 

1603 else: 

1604 return self._criterion_exists() 

1605 elif self.property.uselist: 

1606 raise sa_exc.InvalidRequestError( 

1607 "Can't compare a collection" 

1608 " to an object or collection; use " 

1609 "contains() to test for membership." 

1610 ) 

1611 else: 

1612 return _orm_annotate(self.__negated_contains_or_equals(other)) 

1613 

1614 @util.memoized_property 

1615 def property(self): 

1616 if mapperlib.Mapper._new_mappers: 

1617 mapperlib.Mapper._configure_all() 

1618 return self.prop 

1619 

1620 def _with_parent(self, instance, alias_secondary=True, from_entity=None): 

1621 assert instance is not None 

1622 adapt_source = None 

1623 if from_entity is not None: 

1624 insp = inspect(from_entity) 

1625 if insp.is_aliased_class: 

1626 adapt_source = insp._adapter.adapt_clause 

1627 return self._optimized_compare( 

1628 instance, 

1629 value_is_parent=True, 

1630 adapt_source=adapt_source, 

1631 alias_secondary=alias_secondary, 

1632 ) 

1633 

1634 def _optimized_compare( 

1635 self, 

1636 state, 

1637 value_is_parent=False, 

1638 adapt_source=None, 

1639 alias_secondary=True, 

1640 ): 

1641 if state is not None: 

1642 try: 

1643 state = inspect(state) 

1644 except sa_exc.NoInspectionAvailable: 

1645 state = None 

1646 

1647 if state is None or not getattr(state, "is_instance", False): 

1648 raise sa_exc.ArgumentError( 

1649 "Mapped instance expected for relationship " 

1650 "comparison to object. Classes, queries and other " 

1651 "SQL elements are not accepted in this context; for " 

1652 "comparison with a subquery, " 

1653 "use %s.has(**criteria)." % self 

1654 ) 

1655 reverse_direction = not value_is_parent 

1656 

1657 if state is None: 

1658 return self._lazy_none_clause( 

1659 reverse_direction, adapt_source=adapt_source 

1660 ) 

1661 

1662 if not reverse_direction: 

1663 criterion, bind_to_col = ( 

1664 self._lazy_strategy._lazywhere, 

1665 self._lazy_strategy._bind_to_col, 

1666 ) 

1667 else: 

1668 criterion, bind_to_col = ( 

1669 self._lazy_strategy._rev_lazywhere, 

1670 self._lazy_strategy._rev_bind_to_col, 

1671 ) 

1672 

1673 if reverse_direction: 

1674 mapper = self.mapper 

1675 else: 

1676 mapper = self.parent 

1677 

1678 dict_ = attributes.instance_dict(state.obj()) 

1679 

1680 def visit_bindparam(bindparam): 

1681 if bindparam._identifying_key in bind_to_col: 

1682 bindparam.callable = self._get_attr_w_warn_on_none( 

1683 mapper, 

1684 state, 

1685 dict_, 

1686 bind_to_col[bindparam._identifying_key], 

1687 ) 

1688 

1689 if self.secondary is not None and alias_secondary: 

1690 criterion = ClauseAdapter(self.secondary.alias()).traverse( 

1691 criterion 

1692 ) 

1693 

1694 criterion = visitors.cloned_traverse( 

1695 criterion, {}, {"bindparam": visit_bindparam} 

1696 ) 

1697 

1698 if adapt_source: 

1699 criterion = adapt_source(criterion) 

1700 return criterion 

1701 

1702 def _get_attr_w_warn_on_none(self, mapper, state, dict_, column): 

1703 """Create the callable that is used in a many-to-one expression. 

1704 

1705 E.g.:: 

1706 

1707 u1 = s.query(User).get(5) 

1708 

1709 expr = Address.user == u1 

1710 

1711 Above, the SQL should be "address.user_id = 5". The callable 

1712 returned by this method produces the value "5" based on the identity 

1713 of ``u1``. 

1714 

1715 """ 

1716 

1717 # in this callable, we're trying to thread the needle through 

1718 # a wide variety of scenarios, including: 

1719 # 

1720 # * the object hasn't been flushed yet and there's no value for 

1721 # the attribute as of yet 

1722 # 

1723 # * the object hasn't been flushed yet but it has a user-defined 

1724 # value 

1725 # 

1726 # * the object has a value but it's expired and not locally present 

1727 # 

1728 # * the object has a value but it's expired and not locally present, 

1729 # and the object is also detached 

1730 # 

1731 # * The object hadn't been flushed yet, there was no value, but 

1732 # later, the object has been expired and detached, and *now* 

1733 # they're trying to evaluate it 

1734 # 

1735 # * the object had a value, but it was changed to a new value, and 

1736 # then expired 

1737 # 

1738 # * the object had a value, but it was changed to a new value, and 

1739 # then expired, then the object was detached 

1740 # 

1741 # * the object has a user-set value, but it's None and we don't do 

1742 # the comparison correctly for that so warn 

1743 # 

1744 

1745 prop = mapper.get_property_by_column(column) 

1746 

1747 # by invoking this method, InstanceState will track the last known 

1748 # value for this key each time the attribute is to be expired. 

1749 # this feature was added explicitly for use in this method. 

1750 state._track_last_known_value(prop.key) 

1751 

1752 def _go(): 

1753 last_known = to_return = state._last_known_values[prop.key] 

1754 existing_is_available = last_known is not attributes.NO_VALUE 

1755 

1756 # we support that the value may have changed. so here we 

1757 # try to get the most recent value including re-fetching. 

1758 # only if we can't get a value now due to detachment do we return 

1759 # the last known value 

1760 current_value = mapper._get_state_attr_by_column( 

1761 state, 

1762 dict_, 

1763 column, 

1764 passive=attributes.PASSIVE_OFF 

1765 if state.persistent 

1766 else attributes.PASSIVE_NO_FETCH ^ attributes.INIT_OK, 

1767 ) 

1768 

1769 if current_value is attributes.NEVER_SET: 

1770 if not existing_is_available: 

1771 raise sa_exc.InvalidRequestError( 

1772 "Can't resolve value for column %s on object " 

1773 "%s; no value has been set for this column" 

1774 % (column, state_str(state)) 

1775 ) 

1776 elif current_value is attributes.PASSIVE_NO_RESULT: 

1777 if not existing_is_available: 

1778 raise sa_exc.InvalidRequestError( 

1779 "Can't resolve value for column %s on object " 

1780 "%s; the object is detached and the value was " 

1781 "expired" % (column, state_str(state)) 

1782 ) 

1783 else: 

1784 to_return = current_value 

1785 if to_return is None: 

1786 util.warn( 

1787 "Got None for value of column %s; this is unsupported " 

1788 "for a relationship comparison and will not " 

1789 "currently produce an IS comparison " 

1790 "(but may in a future release)" % column 

1791 ) 

1792 return to_return 

1793 

1794 return _go 

1795 

1796 def _lazy_none_clause(self, reverse_direction=False, adapt_source=None): 

1797 if not reverse_direction: 

1798 criterion, bind_to_col = ( 

1799 self._lazy_strategy._lazywhere, 

1800 self._lazy_strategy._bind_to_col, 

1801 ) 

1802 else: 

1803 criterion, bind_to_col = ( 

1804 self._lazy_strategy._rev_lazywhere, 

1805 self._lazy_strategy._rev_bind_to_col, 

1806 ) 

1807 

1808 criterion = adapt_criterion_to_null(criterion, bind_to_col) 

1809 

1810 if adapt_source: 

1811 criterion = adapt_source(criterion) 

1812 return criterion 

1813 

1814 def __str__(self): 

1815 return str(self.parent.class_.__name__) + "." + self.key 

1816 

1817 def merge( 

1818 self, 

1819 session, 

1820 source_state, 

1821 source_dict, 

1822 dest_state, 

1823 dest_dict, 

1824 load, 

1825 _recursive, 

1826 _resolve_conflict_map, 

1827 ): 

1828 

1829 if load: 

1830 for r in self._reverse_property: 

1831 if (source_state, r) in _recursive: 

1832 return 

1833 

1834 if "merge" not in self._cascade: 

1835 return 

1836 

1837 if self.key not in source_dict: 

1838 return 

1839 

1840 if self.uselist: 

1841 instances = source_state.get_impl(self.key).get( 

1842 source_state, source_dict 

1843 ) 

1844 if hasattr(instances, "_sa_adapter"): 

1845 # convert collections to adapters to get a true iterator 

1846 instances = instances._sa_adapter 

1847 

1848 if load: 

1849 # for a full merge, pre-load the destination collection, 

1850 # so that individual _merge of each item pulls from identity 

1851 # map for those already present. 

1852 # also assumes CollectionAttributeImpl behavior of loading 

1853 # "old" list in any case 

1854 dest_state.get_impl(self.key).get(dest_state, dest_dict) 

1855 

1856 dest_list = [] 

1857 for current in instances: 

1858 current_state = attributes.instance_state(current) 

1859 current_dict = attributes.instance_dict(current) 

1860 _recursive[(current_state, self)] = True 

1861 obj = session._merge( 

1862 current_state, 

1863 current_dict, 

1864 load=load, 

1865 _recursive=_recursive, 

1866 _resolve_conflict_map=_resolve_conflict_map, 

1867 ) 

1868 if obj is not None: 

1869 dest_list.append(obj) 

1870 

1871 if not load: 

1872 coll = attributes.init_state_collection( 

1873 dest_state, dest_dict, self.key 

1874 ) 

1875 for c in dest_list: 

1876 coll.append_without_event(c) 

1877 else: 

1878 dest_state.get_impl(self.key).set( 

1879 dest_state, dest_dict, dest_list, _adapt=False 

1880 ) 

1881 else: 

1882 current = source_dict[self.key] 

1883 if current is not None: 

1884 current_state = attributes.instance_state(current) 

1885 current_dict = attributes.instance_dict(current) 

1886 _recursive[(current_state, self)] = True 

1887 obj = session._merge( 

1888 current_state, 

1889 current_dict, 

1890 load=load, 

1891 _recursive=_recursive, 

1892 _resolve_conflict_map=_resolve_conflict_map, 

1893 ) 

1894 else: 

1895 obj = None 

1896 

1897 if not load: 

1898 dest_dict[self.key] = obj 

1899 else: 

1900 dest_state.get_impl(self.key).set( 

1901 dest_state, dest_dict, obj, None 

1902 ) 

1903 

1904 def _value_as_iterable( 

1905 self, state, dict_, key, passive=attributes.PASSIVE_OFF 

1906 ): 

1907 """Return a list of tuples (state, obj) for the given 

1908 key. 

1909 

1910 returns an empty list if the value is None/empty/PASSIVE_NO_RESULT 

1911 """ 

1912 

1913 impl = state.manager[key].impl 

1914 x = impl.get(state, dict_, passive=passive) 

1915 if x is attributes.PASSIVE_NO_RESULT or x is None: 

1916 return [] 

1917 elif hasattr(impl, "get_collection"): 

1918 return [ 

1919 (attributes.instance_state(o), o) 

1920 for o in impl.get_collection(state, dict_, x, passive=passive) 

1921 ] 

1922 else: 

1923 return [(attributes.instance_state(x), x)] 

1924 

1925 def cascade_iterator( 

1926 self, type_, state, dict_, visited_states, halt_on=None 

1927 ): 

1928 # assert type_ in self._cascade 

1929 

1930 # only actively lazy load on the 'delete' cascade 

1931 if type_ != "delete" or self.passive_deletes: 

1932 passive = attributes.PASSIVE_NO_INITIALIZE 

1933 else: 

1934 passive = attributes.PASSIVE_OFF 

1935 

1936 if type_ == "save-update": 

1937 tuples = state.manager[self.key].impl.get_all_pending(state, dict_) 

1938 

1939 else: 

1940 tuples = self._value_as_iterable( 

1941 state, dict_, self.key, passive=passive 

1942 ) 

1943 

1944 skip_pending = ( 

1945 type_ == "refresh-expire" and "delete-orphan" not in self._cascade 

1946 ) 

1947 

1948 for instance_state, c in tuples: 

1949 if instance_state in visited_states: 

1950 continue 

1951 

1952 if c is None: 

1953 # would like to emit a warning here, but 

1954 # would not be consistent with collection.append(None) 

1955 # current behavior of silently skipping. 

1956 # see [ticket:2229] 

1957 continue 

1958 

1959 instance_dict = attributes.instance_dict(c) 

1960 

1961 if halt_on and halt_on(instance_state): 

1962 continue 

1963 

1964 if skip_pending and not instance_state.key: 

1965 continue 

1966 

1967 instance_mapper = instance_state.manager.mapper 

1968 

1969 if not instance_mapper.isa(self.mapper.class_manager.mapper): 

1970 raise AssertionError( 

1971 "Attribute '%s' on class '%s' " 

1972 "doesn't handle objects " 

1973 "of type '%s'" 

1974 % (self.key, self.parent.class_, c.__class__) 

1975 ) 

1976 

1977 visited_states.add(instance_state) 

1978 

1979 yield c, instance_mapper, instance_state, instance_dict 

1980 

1981 @property 

1982 def _effective_sync_backref(self): 

1983 return self.sync_backref is not False 

1984 

1985 @staticmethod 

1986 def _check_sync_backref(rel_a, rel_b): 

1987 if rel_a.viewonly and rel_b.sync_backref: 

1988 raise sa_exc.InvalidRequestError( 

1989 "Relationship %s cannot specify sync_backref=True since %s " 

1990 "includes viewonly=True." % (rel_b, rel_a) 

1991 ) 

1992 if rel_a.viewonly and rel_b.sync_backref is not False: 

1993 util.warn_limited( 

1994 "Setting backref / back_populates on relationship %s to refer " 

1995 "to viewonly relationship %s should include " 

1996 "sync_backref=False set on the %s relationship. ", 

1997 (rel_b, rel_a, rel_b), 

1998 ) 

1999 

2000 def _add_reverse_property(self, key): 

2001 other = self.mapper.get_property(key, _configure_mappers=False) 

2002 # viewonly and sync_backref cases 

2003 # 1. self.viewonly==True and other.sync_backref==True -> error 

2004 # 2. self.viewonly==True and other.viewonly==False and 

2005 # other.sync_backref==None -> warn sync_backref=False, set to False 

2006 self._check_sync_backref(self, other) 

2007 # 3. other.viewonly==True and self.sync_backref==True -> error 

2008 # 4. other.viewonly==True and self.viewonly==False and 

2009 # self.sync_backref==None -> warn sync_backref=False, set to False 

2010 self._check_sync_backref(other, self) 

2011 

2012 self._reverse_property.add(other) 

2013 other._reverse_property.add(self) 

2014 

2015 if not other.mapper.common_parent(self.parent): 

2016 raise sa_exc.ArgumentError( 

2017 "reverse_property %r on " 

2018 "relationship %s references relationship %s, which " 

2019 "does not reference mapper %s" 

2020 % (key, self, other, self.parent) 

2021 ) 

2022 

2023 if ( 

2024 self.direction in (ONETOMANY, MANYTOONE) 

2025 and self.direction == other.direction 

2026 ): 

2027 raise sa_exc.ArgumentError( 

2028 "%s and back-reference %s are " 

2029 "both of the same direction %r. Did you mean to " 

2030 "set remote_side on the many-to-one side ?" 

2031 % (other, self, self.direction) 

2032 ) 

2033 

2034 @util.memoized_property 

2035 def entity(self): # type: () -> Union[AliasedInsp, Mapper] 

2036 """Return the target mapped entity, which is an inspect() of the 

2037 class or aliased class tha is referred towards. 

2038 

2039 """ 

2040 if util.callable(self.argument) and not isinstance( 

2041 self.argument, (type, mapperlib.Mapper) 

2042 ): 

2043 argument = self.argument() 

2044 else: 

2045 argument = self.argument 

2046 

2047 if isinstance(argument, type): 

2048 return mapperlib.class_mapper(argument, configure=False) 

2049 

2050 try: 

2051 entity = inspect(argument) 

2052 except sa_exc.NoInspectionAvailable: 

2053 pass 

2054 else: 

2055 if hasattr(entity, "mapper"): 

2056 return entity 

2057 

2058 raise sa_exc.ArgumentError( 

2059 "relationship '%s' expects " 

2060 "a class or a mapper argument (received: %s)" 

2061 % (self.key, type(argument)) 

2062 ) 

2063 

2064 @util.memoized_property 

2065 def mapper(self): 

2066 """Return the targeted :class:`_orm.Mapper` for this 

2067 :class:`.RelationshipProperty`. 

2068 

2069 This is a lazy-initializing static attribute. 

2070 

2071 """ 

2072 return self.entity.mapper 

2073 

2074 def do_init(self): 

2075 self._check_conflicts() 

2076 self._process_dependent_arguments() 

2077 self._setup_join_conditions() 

2078 self._check_cascade_settings(self._cascade) 

2079 self._post_init() 

2080 self._generate_backref() 

2081 self._join_condition._warn_for_conflicting_sync_targets() 

2082 super(RelationshipProperty, self).do_init() 

2083 self._lazy_strategy = self._get_strategy((("lazy", "select"),)) 

2084 

2085 def _process_dependent_arguments(self): 

2086 """Convert incoming configuration arguments to their 

2087 proper form. 

2088 

2089 Callables are resolved, ORM annotations removed. 

2090 

2091 """ 

2092 # accept callables for other attributes which may require 

2093 # deferred initialization. This technique is used 

2094 # by declarative "string configs" and some recipes. 

2095 for attr in ( 

2096 "order_by", 

2097 "primaryjoin", 

2098 "secondaryjoin", 

2099 "secondary", 

2100 "_user_defined_foreign_keys", 

2101 "remote_side", 

2102 ): 

2103 attr_value = getattr(self, attr) 

2104 if util.callable(attr_value): 

2105 setattr(self, attr, attr_value()) 

2106 

2107 # remove "annotations" which are present if mapped class 

2108 # descriptors are used to create the join expression. 

2109 for attr in "primaryjoin", "secondaryjoin": 

2110 val = getattr(self, attr) 

2111 if val is not None: 

2112 setattr( 

2113 self, 

2114 attr, 

2115 _orm_deannotate( 

2116 expression._only_column_elements(val, attr) 

2117 ), 

2118 ) 

2119 

2120 # ensure expressions in self.order_by, foreign_keys, 

2121 # remote_side are all columns, not strings. 

2122 if self.order_by is not False and self.order_by is not None: 

2123 self.order_by = [ 

2124 expression._only_column_elements(x, "order_by") 

2125 for x in util.to_list(self.order_by) 

2126 ] 

2127 

2128 self._user_defined_foreign_keys = util.column_set( 

2129 expression._only_column_elements(x, "foreign_keys") 

2130 for x in util.to_column_set(self._user_defined_foreign_keys) 

2131 ) 

2132 

2133 self.remote_side = util.column_set( 

2134 expression._only_column_elements(x, "remote_side") 

2135 for x in util.to_column_set(self.remote_side) 

2136 ) 

2137 

2138 self.target = self.entity.persist_selectable 

2139 

2140 def _setup_join_conditions(self): 

2141 self._join_condition = jc = JoinCondition( 

2142 parent_persist_selectable=self.parent.persist_selectable, 

2143 child_persist_selectable=self.entity.persist_selectable, 

2144 parent_local_selectable=self.parent.local_table, 

2145 child_local_selectable=self.entity.local_table, 

2146 primaryjoin=self.primaryjoin, 

2147 secondary=self.secondary, 

2148 secondaryjoin=self.secondaryjoin, 

2149 parent_equivalents=self.parent._equivalent_columns, 

2150 child_equivalents=self.mapper._equivalent_columns, 

2151 consider_as_foreign_keys=self._user_defined_foreign_keys, 

2152 local_remote_pairs=self.local_remote_pairs, 

2153 remote_side=self.remote_side, 

2154 self_referential=self._is_self_referential, 

2155 prop=self, 

2156 support_sync=not self.viewonly, 

2157 can_be_synced_fn=self._columns_are_mapped, 

2158 ) 

2159 self.primaryjoin = jc.primaryjoin 

2160 self.secondaryjoin = jc.secondaryjoin 

2161 self.direction = jc.direction 

2162 self.local_remote_pairs = jc.local_remote_pairs 

2163 self.remote_side = jc.remote_columns 

2164 self.local_columns = jc.local_columns 

2165 self.synchronize_pairs = jc.synchronize_pairs 

2166 self._calculated_foreign_keys = jc.foreign_key_columns 

2167 self.secondary_synchronize_pairs = jc.secondary_synchronize_pairs 

2168 

2169 def _check_conflicts(self): 

2170 """Test that this relationship is legal, warn about 

2171 inheritance conflicts.""" 

2172 

2173 if self.parent.non_primary and not mapperlib.class_mapper( 

2174 self.parent.class_, configure=False 

2175 ).has_property(self.key): 

2176 raise sa_exc.ArgumentError( 

2177 "Attempting to assign a new " 

2178 "relationship '%s' to a non-primary mapper on " 

2179 "class '%s'. New relationships can only be added " 

2180 "to the primary mapper, i.e. the very first mapper " 

2181 "created for class '%s' " 

2182 % ( 

2183 self.key, 

2184 self.parent.class_.__name__, 

2185 self.parent.class_.__name__, 

2186 ) 

2187 ) 

2188 

2189 @property 

2190 def cascade(self): 

2191 """Return the current cascade setting for this 

2192 :class:`.RelationshipProperty`. 

2193 """ 

2194 return self._cascade 

2195 

2196 @cascade.setter 

2197 def cascade(self, cascade): 

2198 self._set_cascade(cascade) 

2199 

2200 def _set_cascade(self, cascade, warn=True): 

2201 cascade = CascadeOptions(cascade) 

2202 

2203 if warn and self.viewonly: 

2204 non_viewonly = set(cascade).difference( 

2205 CascadeOptions._viewonly_cascades 

2206 ) 

2207 if non_viewonly: 

2208 # we are warning here rather than warn deprecated as this 

2209 # setting actively does the wrong thing and Python shows 

2210 # regular warnings more aggressively than deprecation warnings 

2211 # by default. There's no other guard against setting active 

2212 # persistence cascades under viewonly=True so this will raise 

2213 # in 1.4. 

2214 util.warn( 

2215 'Cascade settings "%s" should not be combined with a ' 

2216 "viewonly=True relationship. This configuration will " 

2217 "raise an error in version 1.4. Note that in versions " 

2218 "prior to 1.4, " 

2219 "these cascade settings may still produce a mutating " 

2220 "effect even though this relationship is marked as " 

2221 "viewonly=True." % (", ".join(sorted(non_viewonly))) 

2222 ) 

2223 

2224 if "mapper" in self.__dict__: 

2225 self._check_cascade_settings(cascade) 

2226 self._cascade = cascade 

2227 

2228 if self._dependency_processor: 

2229 self._dependency_processor.cascade = cascade 

2230 

2231 def _check_cascade_settings(self, cascade): 

2232 if ( 

2233 cascade.delete_orphan 

2234 and not self.single_parent 

2235 and (self.direction is MANYTOMANY or self.direction is MANYTOONE) 

2236 ): 

2237 raise sa_exc.ArgumentError( 

2238 "For %(direction)s relationship %(rel)s, delete-orphan " 

2239 "cascade is normally " 

2240 'configured only on the "one" side of a one-to-many ' 

2241 "relationship, " 

2242 'and not on the "many" side of a many-to-one or many-to-many ' 

2243 "relationship. " 

2244 "To force this relationship to allow a particular " 

2245 '"%(relatedcls)s" object to be referred towards by only ' 

2246 'a single "%(clsname)s" object at a time via the ' 

2247 "%(rel)s relationship, which " 

2248 "would allow " 

2249 "delete-orphan cascade to take place in this direction, set " 

2250 "the single_parent=True flag." 

2251 % { 

2252 "rel": self, 

2253 "direction": "many-to-one" 

2254 if self.direction is MANYTOONE 

2255 else "many-to-many", 

2256 "clsname": self.parent.class_.__name__, 

2257 "relatedcls": self.mapper.class_.__name__, 

2258 }, 

2259 code="bbf0", 

2260 ) 

2261 

2262 if self.direction is MANYTOONE and self.passive_deletes: 

2263 util.warn( 

2264 "On %s, 'passive_deletes' is normally configured " 

2265 "on one-to-many, one-to-one, many-to-many " 

2266 "relationships only." % self 

2267 ) 

2268 

2269 if self.passive_deletes == "all" and ( 

2270 "delete" in cascade or "delete-orphan" in cascade 

2271 ): 

2272 raise sa_exc.ArgumentError( 

2273 "On %s, can't set passive_deletes='all' in conjunction " 

2274 "with 'delete' or 'delete-orphan' cascade" % self 

2275 ) 

2276 

2277 if cascade.delete_orphan: 

2278 self.mapper.primary_mapper()._delete_orphans.append( 

2279 (self.key, self.parent.class_) 

2280 ) 

2281 

2282 def _persists_for(self, mapper): 

2283 """Return True if this property will persist values on behalf 

2284 of the given mapper. 

2285 

2286 """ 

2287 

2288 return ( 

2289 self.key in mapper.relationships 

2290 and mapper.relationships[self.key] is self 

2291 ) 

2292 

2293 def _columns_are_mapped(self, *cols): 

2294 """Return True if all columns in the given collection are 

2295 mapped by the tables referenced by this :class:`.Relationship`. 

2296 

2297 """ 

2298 for c in cols: 

2299 if ( 

2300 self.secondary is not None 

2301 and self.secondary.c.contains_column(c) 

2302 ): 

2303 continue 

2304 if not self.parent.persist_selectable.c.contains_column( 

2305 c 

2306 ) and not self.target.c.contains_column(c): 

2307 return False 

2308 return True 

2309 

2310 def _generate_backref(self): 

2311 """Interpret the 'backref' instruction to create a 

2312 :func:`_orm.relationship` complementary to this one.""" 

2313 

2314 if self.parent.non_primary: 

2315 return 

2316 if self.backref is not None and not self.back_populates: 

2317 if isinstance(self.backref, util.string_types): 

2318 backref_key, kwargs = self.backref, {} 

2319 else: 

2320 backref_key, kwargs = self.backref 

2321 mapper = self.mapper.primary_mapper() 

2322 

2323 if not mapper.concrete: 

2324 check = set(mapper.iterate_to_root()).union( 

2325 mapper.self_and_descendants 

2326 ) 

2327 for m in check: 

2328 if m.has_property(backref_key) and not m.concrete: 

2329 raise sa_exc.ArgumentError( 

2330 "Error creating backref " 

2331 "'%s' on relationship '%s': property of that " 

2332 "name exists on mapper '%s'" 

2333 % (backref_key, self, m) 

2334 ) 

2335 

2336 # determine primaryjoin/secondaryjoin for the 

2337 # backref. Use the one we had, so that 

2338 # a custom join doesn't have to be specified in 

2339 # both directions. 

2340 if self.secondary is not None: 

2341 # for many to many, just switch primaryjoin/ 

2342 # secondaryjoin. use the annotated 

2343 # pj/sj on the _join_condition. 

2344 pj = kwargs.pop( 

2345 "primaryjoin", 

2346 self._join_condition.secondaryjoin_minus_local, 

2347 ) 

2348 sj = kwargs.pop( 

2349 "secondaryjoin", 

2350 self._join_condition.primaryjoin_minus_local, 

2351 ) 

2352 else: 

2353 pj = kwargs.pop( 

2354 "primaryjoin", 

2355 self._join_condition.primaryjoin_reverse_remote, 

2356 ) 

2357 sj = kwargs.pop("secondaryjoin", None) 

2358 if sj: 

2359 raise sa_exc.InvalidRequestError( 

2360 "Can't assign 'secondaryjoin' on a backref " 

2361 "against a non-secondary relationship." 

2362 ) 

2363 

2364 foreign_keys = kwargs.pop( 

2365 "foreign_keys", self._user_defined_foreign_keys 

2366 ) 

2367 parent = self.parent.primary_mapper() 

2368 kwargs.setdefault("viewonly", self.viewonly) 

2369 kwargs.setdefault("post_update", self.post_update) 

2370 kwargs.setdefault("passive_updates", self.passive_updates) 

2371 kwargs.setdefault("sync_backref", self.sync_backref) 

2372 self.back_populates = backref_key 

2373 relationship = RelationshipProperty( 

2374 parent, 

2375 self.secondary, 

2376 pj, 

2377 sj, 

2378 foreign_keys=foreign_keys, 

2379 back_populates=self.key, 

2380 **kwargs 

2381 ) 

2382 mapper._configure_property(backref_key, relationship) 

2383 

2384 if self.back_populates: 

2385 self._add_reverse_property(self.back_populates) 

2386 

2387 def _post_init(self): 

2388 if self.uselist is None: 

2389 self.uselist = self.direction is not MANYTOONE 

2390 if not self.viewonly: 

2391 self._dependency_processor = ( 

2392 dependency.DependencyProcessor.from_relationship 

2393 )(self) 

2394 

2395 @util.memoized_property 

2396 def _use_get(self): 

2397 """memoize the 'use_get' attribute of this RelationshipLoader's 

2398 lazyloader.""" 

2399 

2400 strategy = self._lazy_strategy 

2401 return strategy.use_get 

2402 

2403 @util.memoized_property 

2404 def _is_self_referential(self): 

2405 return self.mapper.common_parent(self.parent) 

2406 

2407 def _create_joins( 

2408 self, 

2409 source_polymorphic=False, 

2410 source_selectable=None, 

2411 dest_polymorphic=False, 

2412 dest_selectable=None, 

2413 of_type_mapper=None, 

2414 alias_secondary=False, 

2415 ): 

2416 

2417 aliased = False 

2418 

2419 if alias_secondary and self.secondary is not None: 

2420 aliased = True 

2421 

2422 if source_selectable is None: 

2423 if source_polymorphic and self.parent.with_polymorphic: 

2424 source_selectable = self.parent._with_polymorphic_selectable 

2425 

2426 if dest_selectable is None: 

2427 dest_selectable = self.entity.selectable 

2428 if dest_polymorphic and self.mapper.with_polymorphic: 

2429 aliased = True 

2430 

2431 if self._is_self_referential and source_selectable is None: 

2432 dest_selectable = dest_selectable.alias() 

2433 aliased = True 

2434 elif ( 

2435 dest_selectable is not self.mapper._with_polymorphic_selectable 

2436 or self.mapper.with_polymorphic 

2437 ): 

2438 aliased = True 

2439 

2440 dest_mapper = of_type_mapper or self.mapper 

2441 

2442 single_crit = dest_mapper._single_table_criterion 

2443 aliased = aliased or ( 

2444 source_selectable is not None 

2445 and ( 

2446 source_selectable 

2447 is not self.parent._with_polymorphic_selectable 

2448 or source_selectable._is_from_container # e.g an alias 

2449 ) 

2450 ) 

2451 

2452 ( 

2453 primaryjoin, 

2454 secondaryjoin, 

2455 secondary, 

2456 target_adapter, 

2457 dest_selectable, 

2458 ) = self._join_condition.join_targets( 

2459 source_selectable, dest_selectable, aliased, single_crit 

2460 ) 

2461 if source_selectable is None: 

2462 source_selectable = self.parent.local_table 

2463 if dest_selectable is None: 

2464 dest_selectable = self.entity.local_table 

2465 return ( 

2466 primaryjoin, 

2467 secondaryjoin, 

2468 source_selectable, 

2469 dest_selectable, 

2470 secondary, 

2471 target_adapter, 

2472 ) 

2473 

2474 

2475def _annotate_columns(element, annotations): 

2476 def clone(elem): 

2477 if isinstance(elem, expression.ColumnClause): 

2478 elem = elem._annotate(annotations.copy()) 

2479 elem._copy_internals(clone=clone) 

2480 return elem 

2481 

2482 if element is not None: 

2483 element = clone(element) 

2484 clone = None # remove gc cycles 

2485 return element 

2486 

2487 

2488class JoinCondition(object): 

2489 def __init__( 

2490 self, 

2491 parent_persist_selectable, 

2492 child_persist_selectable, 

2493 parent_local_selectable, 

2494 child_local_selectable, 

2495 primaryjoin=None, 

2496 secondary=None, 

2497 secondaryjoin=None, 

2498 parent_equivalents=None, 

2499 child_equivalents=None, 

2500 consider_as_foreign_keys=None, 

2501 local_remote_pairs=None, 

2502 remote_side=None, 

2503 self_referential=False, 

2504 prop=None, 

2505 support_sync=True, 

2506 can_be_synced_fn=lambda *c: True, 

2507 ): 

2508 self.parent_persist_selectable = parent_persist_selectable 

2509 self.parent_local_selectable = parent_local_selectable 

2510 self.child_persist_selectable = child_persist_selectable 

2511 self.child_local_selectable = child_local_selectable 

2512 self.parent_equivalents = parent_equivalents 

2513 self.child_equivalents = child_equivalents 

2514 self.primaryjoin = primaryjoin 

2515 self.secondaryjoin = secondaryjoin 

2516 self.secondary = secondary 

2517 self.consider_as_foreign_keys = consider_as_foreign_keys 

2518 self._local_remote_pairs = local_remote_pairs 

2519 self._remote_side = remote_side 

2520 self.prop = prop 

2521 self.self_referential = self_referential 

2522 self.support_sync = support_sync 

2523 self.can_be_synced_fn = can_be_synced_fn 

2524 self._determine_joins() 

2525 self._sanitize_joins() 

2526 self._annotate_fks() 

2527 self._annotate_remote() 

2528 self._annotate_local() 

2529 self._annotate_parentmapper() 

2530 self._setup_pairs() 

2531 self._check_foreign_cols(self.primaryjoin, True) 

2532 if self.secondaryjoin is not None: 

2533 self._check_foreign_cols(self.secondaryjoin, False) 

2534 self._determine_direction() 

2535 self._check_remote_side() 

2536 self._log_joins() 

2537 

2538 def _log_joins(self): 

2539 if self.prop is None: 

2540 return 

2541 log = self.prop.logger 

2542 log.info("%s setup primary join %s", self.prop, self.primaryjoin) 

2543 log.info("%s setup secondary join %s", self.prop, self.secondaryjoin) 

2544 log.info( 

2545 "%s synchronize pairs [%s]", 

2546 self.prop, 

2547 ",".join( 

2548 "(%s => %s)" % (l, r) for (l, r) in self.synchronize_pairs 

2549 ), 

2550 ) 

2551 log.info( 

2552 "%s secondary synchronize pairs [%s]", 

2553 self.prop, 

2554 ",".join( 

2555 "(%s => %s)" % (l, r) 

2556 for (l, r) in self.secondary_synchronize_pairs or [] 

2557 ), 

2558 ) 

2559 log.info( 

2560 "%s local/remote pairs [%s]", 

2561 self.prop, 

2562 ",".join( 

2563 "(%s / %s)" % (l, r) for (l, r) in self.local_remote_pairs 

2564 ), 

2565 ) 

2566 log.info( 

2567 "%s remote columns [%s]", 

2568 self.prop, 

2569 ",".join("%s" % col for col in self.remote_columns), 

2570 ) 

2571 log.info( 

2572 "%s local columns [%s]", 

2573 self.prop, 

2574 ",".join("%s" % col for col in self.local_columns), 

2575 ) 

2576 log.info("%s relationship direction %s", self.prop, self.direction) 

2577 

2578 def _sanitize_joins(self): 

2579 """remove the parententity annotation from our join conditions which 

2580 can leak in here based on some declarative patterns and maybe others. 

2581 

2582 We'd want to remove "parentmapper" also, but apparently there's 

2583 an exotic use case in _join_fixture_inh_selfref_w_entity 

2584 that relies upon it being present, see :ticket:`3364`. 

2585 

2586 """ 

2587 

2588 self.primaryjoin = _deep_deannotate( 

2589 self.primaryjoin, values=("parententity",) 

2590 ) 

2591 if self.secondaryjoin is not None: 

2592 self.secondaryjoin = _deep_deannotate( 

2593 self.secondaryjoin, values=("parententity",) 

2594 ) 

2595 

2596 def _determine_joins(self): 

2597 """Determine the 'primaryjoin' and 'secondaryjoin' attributes, 

2598 if not passed to the constructor already. 

2599 

2600 This is based on analysis of the foreign key relationships 

2601 between the parent and target mapped selectables. 

2602 

2603 """ 

2604 if self.secondaryjoin is not None and self.secondary is None: 

2605 raise sa_exc.ArgumentError( 

2606 "Property %s specified with secondary " 

2607 "join condition but " 

2608 "no secondary argument" % self.prop 

2609 ) 

2610 

2611 # find a join between the given mapper's mapped table and 

2612 # the given table. will try the mapper's local table first 

2613 # for more specificity, then if not found will try the more 

2614 # general mapped table, which in the case of inheritance is 

2615 # a join. 

2616 try: 

2617 consider_as_foreign_keys = self.consider_as_foreign_keys or None 

2618 if self.secondary is not None: 

2619 if self.secondaryjoin is None: 

2620 self.secondaryjoin = join_condition( 

2621 self.child_persist_selectable, 

2622 self.secondary, 

2623 a_subset=self.child_local_selectable, 

2624 consider_as_foreign_keys=consider_as_foreign_keys, 

2625 ) 

2626 if self.primaryjoin is None: 

2627 self.primaryjoin = join_condition( 

2628 self.parent_persist_selectable, 

2629 self.secondary, 

2630 a_subset=self.parent_local_selectable, 

2631 consider_as_foreign_keys=consider_as_foreign_keys, 

2632 ) 

2633 else: 

2634 if self.primaryjoin is None: 

2635 self.primaryjoin = join_condition( 

2636 self.parent_persist_selectable, 

2637 self.child_persist_selectable, 

2638 a_subset=self.parent_local_selectable, 

2639 consider_as_foreign_keys=consider_as_foreign_keys, 

2640 ) 

2641 except sa_exc.NoForeignKeysError as nfe: 

2642 if self.secondary is not None: 

2643 util.raise_( 

2644 sa_exc.NoForeignKeysError( 

2645 "Could not determine join " 

2646 "condition between parent/child tables on " 

2647 "relationship %s - there are no foreign keys " 

2648 "linking these tables via secondary table '%s'. " 

2649 "Ensure that referencing columns are associated " 

2650 "with a ForeignKey or ForeignKeyConstraint, or " 

2651 "specify 'primaryjoin' and 'secondaryjoin' " 

2652 "expressions." % (self.prop, self.secondary) 

2653 ), 

2654 from_=nfe, 

2655 ) 

2656 else: 

2657 util.raise_( 

2658 sa_exc.NoForeignKeysError( 

2659 "Could not determine join " 

2660 "condition between parent/child tables on " 

2661 "relationship %s - there are no foreign keys " 

2662 "linking these tables. " 

2663 "Ensure that referencing columns are associated " 

2664 "with a ForeignKey or ForeignKeyConstraint, or " 

2665 "specify a 'primaryjoin' expression." % self.prop 

2666 ), 

2667 from_=nfe, 

2668 ) 

2669 except sa_exc.AmbiguousForeignKeysError as afe: 

2670 if self.secondary is not None: 

2671 util.raise_( 

2672 sa_exc.AmbiguousForeignKeysError( 

2673 "Could not determine join " 

2674 "condition between parent/child tables on " 

2675 "relationship %s - there are multiple foreign key " 

2676 "paths linking the tables via secondary table '%s'. " 

2677 "Specify the 'foreign_keys' " 

2678 "argument, providing a list of those columns which " 

2679 "should be counted as containing a foreign key " 

2680 "reference from the secondary table to each of the " 

2681 "parent and child tables." 

2682 % (self.prop, self.secondary) 

2683 ), 

2684 from_=afe, 

2685 ) 

2686 else: 

2687 util.raise_( 

2688 sa_exc.AmbiguousForeignKeysError( 

2689 "Could not determine join " 

2690 "condition between parent/child tables on " 

2691 "relationship %s - there are multiple foreign key " 

2692 "paths linking the tables. Specify the " 

2693 "'foreign_keys' argument, providing a list of those " 

2694 "columns which should be counted as containing a " 

2695 "foreign key reference to the parent table." 

2696 % self.prop 

2697 ), 

2698 from_=afe, 

2699 ) 

2700 

2701 @property 

2702 def primaryjoin_minus_local(self): 

2703 return _deep_deannotate(self.primaryjoin, values=("local", "remote")) 

2704 

2705 @property 

2706 def secondaryjoin_minus_local(self): 

2707 return _deep_deannotate(self.secondaryjoin, values=("local", "remote")) 

2708 

2709 @util.memoized_property 

2710 def primaryjoin_reverse_remote(self): 

2711 """Return the primaryjoin condition suitable for the 

2712 "reverse" direction. 

2713 

2714 If the primaryjoin was delivered here with pre-existing 

2715 "remote" annotations, the local/remote annotations 

2716 are reversed. Otherwise, the local/remote annotations 

2717 are removed. 

2718 

2719 """ 

2720 if self._has_remote_annotations: 

2721 

2722 def replace(element): 

2723 if "remote" in element._annotations: 

2724 v = element._annotations.copy() 

2725 del v["remote"] 

2726 v["local"] = True 

2727 return element._with_annotations(v) 

2728 elif "local" in element._annotations: 

2729 v = element._annotations.copy() 

2730 del v["local"] 

2731 v["remote"] = True 

2732 return element._with_annotations(v) 

2733 

2734 return visitors.replacement_traverse(self.primaryjoin, {}, replace) 

2735 else: 

2736 if self._has_foreign_annotations: 

2737 # TODO: coverage 

2738 return _deep_deannotate( 

2739 self.primaryjoin, values=("local", "remote") 

2740 ) 

2741 else: 

2742 return _deep_deannotate(self.primaryjoin) 

2743 

2744 def _has_annotation(self, clause, annotation): 

2745 for col in visitors.iterate(clause, {}): 

2746 if annotation in col._annotations: 

2747 return True 

2748 else: 

2749 return False 

2750 

2751 @util.memoized_property 

2752 def _has_foreign_annotations(self): 

2753 return self._has_annotation(self.primaryjoin, "foreign") 

2754 

2755 @util.memoized_property 

2756 def _has_remote_annotations(self): 

2757 return self._has_annotation(self.primaryjoin, "remote") 

2758 

2759 def _annotate_fks(self): 

2760 """Annotate the primaryjoin and secondaryjoin 

2761 structures with 'foreign' annotations marking columns 

2762 considered as foreign. 

2763 

2764 """ 

2765 if self._has_foreign_annotations: 

2766 return 

2767 

2768 if self.consider_as_foreign_keys: 

2769 self._annotate_from_fk_list() 

2770 else: 

2771 self._annotate_present_fks() 

2772 

2773 def _annotate_from_fk_list(self): 

2774 def check_fk(col): 

2775 if col in self.consider_as_foreign_keys: 

2776 return col._annotate({"foreign": True}) 

2777 

2778 self.primaryjoin = visitors.replacement_traverse( 

2779 self.primaryjoin, {}, check_fk 

2780 ) 

2781 if self.secondaryjoin is not None: 

2782 self.secondaryjoin = visitors.replacement_traverse( 

2783 self.secondaryjoin, {}, check_fk 

2784 ) 

2785 

2786 def _annotate_present_fks(self): 

2787 if self.secondary is not None: 

2788 secondarycols = util.column_set(self.secondary.c) 

2789 else: 

2790 secondarycols = set() 

2791 

2792 def is_foreign(a, b): 

2793 if isinstance(a, schema.Column) and isinstance(b, schema.Column): 

2794 if a.references(b): 

2795 return a 

2796 elif b.references(a): 

2797 return b 

2798 

2799 if secondarycols: 

2800 if a in secondarycols and b not in secondarycols: 

2801 return a 

2802 elif b in secondarycols and a not in secondarycols: 

2803 return b 

2804 

2805 def visit_binary(binary): 

2806 if not isinstance( 

2807 binary.left, sql.ColumnElement 

2808 ) or not isinstance(binary.right, sql.ColumnElement): 

2809 return 

2810 

2811 if ( 

2812 "foreign" not in binary.left._annotations 

2813 and "foreign" not in binary.right._annotations 

2814 ): 

2815 col = is_foreign(binary.left, binary.right) 

2816 if col is not None: 

2817 if col.compare(binary.left): 

2818 binary.left = binary.left._annotate({"foreign": True}) 

2819 elif col.compare(binary.right): 

2820 binary.right = binary.right._annotate( 

2821 {"foreign": True} 

2822 ) 

2823 

2824 self.primaryjoin = visitors.cloned_traverse( 

2825 self.primaryjoin, {}, {"binary": visit_binary} 

2826 ) 

2827 if self.secondaryjoin is not None: 

2828 self.secondaryjoin = visitors.cloned_traverse( 

2829 self.secondaryjoin, {}, {"binary": visit_binary} 

2830 ) 

2831 

2832 def _refers_to_parent_table(self): 

2833 """Return True if the join condition contains column 

2834 comparisons where both columns are in both tables. 

2835 

2836 """ 

2837 pt = self.parent_persist_selectable 

2838 mt = self.child_persist_selectable 

2839 result = [False] 

2840 

2841 def visit_binary(binary): 

2842 c, f = binary.left, binary.right 

2843 if ( 

2844 isinstance(c, expression.ColumnClause) 

2845 and isinstance(f, expression.ColumnClause) 

2846 and pt.is_derived_from(c.table) 

2847 and pt.is_derived_from(f.table) 

2848 and mt.is_derived_from(c.table) 

2849 and mt.is_derived_from(f.table) 

2850 ): 

2851 result[0] = True 

2852 

2853 visitors.traverse(self.primaryjoin, {}, {"binary": visit_binary}) 

2854 return result[0] 

2855 

2856 def _tables_overlap(self): 

2857 """Return True if parent/child tables have some overlap.""" 

2858 

2859 return selectables_overlap( 

2860 self.parent_persist_selectable, self.child_persist_selectable 

2861 ) 

2862 

2863 def _annotate_remote(self): 

2864 """Annotate the primaryjoin and secondaryjoin 

2865 structures with 'remote' annotations marking columns 

2866 considered as part of the 'remote' side. 

2867 

2868 """ 

2869 if self._has_remote_annotations: 

2870 return 

2871 

2872 if self.secondary is not None: 

2873 self._annotate_remote_secondary() 

2874 elif self._local_remote_pairs or self._remote_side: 

2875 self._annotate_remote_from_args() 

2876 elif self._refers_to_parent_table(): 

2877 self._annotate_selfref( 

2878 lambda col: "foreign" in col._annotations, False 

2879 ) 

2880 elif self._tables_overlap(): 

2881 self._annotate_remote_with_overlap() 

2882 else: 

2883 self._annotate_remote_distinct_selectables() 

2884 

2885 def _annotate_remote_secondary(self): 

2886 """annotate 'remote' in primaryjoin, secondaryjoin 

2887 when 'secondary' is present. 

2888 

2889 """ 

2890 

2891 def repl(element): 

2892 if self.secondary.c.contains_column(element): 

2893 return element._annotate({"remote": True}) 

2894 

2895 self.primaryjoin = visitors.replacement_traverse( 

2896 self.primaryjoin, {}, repl 

2897 ) 

2898 self.secondaryjoin = visitors.replacement_traverse( 

2899 self.secondaryjoin, {}, repl 

2900 ) 

2901 

2902 def _annotate_selfref(self, fn, remote_side_given): 

2903 """annotate 'remote' in primaryjoin, secondaryjoin 

2904 when the relationship is detected as self-referential. 

2905 

2906 """ 

2907 

2908 def visit_binary(binary): 

2909 equated = binary.left.compare(binary.right) 

2910 if isinstance(binary.left, expression.ColumnClause) and isinstance( 

2911 binary.right, expression.ColumnClause 

2912 ): 

2913 # assume one to many - FKs are "remote" 

2914 if fn(binary.left): 

2915 binary.left = binary.left._annotate({"remote": True}) 

2916 if fn(binary.right) and not equated: 

2917 binary.right = binary.right._annotate({"remote": True}) 

2918 elif not remote_side_given: 

2919 self._warn_non_column_elements() 

2920 

2921 self.primaryjoin = visitors.cloned_traverse( 

2922 self.primaryjoin, {}, {"binary": visit_binary} 

2923 ) 

2924 

2925 def _annotate_remote_from_args(self): 

2926 """annotate 'remote' in primaryjoin, secondaryjoin 

2927 when the 'remote_side' or '_local_remote_pairs' 

2928 arguments are used. 

2929 

2930 """ 

2931 if self._local_remote_pairs: 

2932 if self._remote_side: 

2933 raise sa_exc.ArgumentError( 

2934 "remote_side argument is redundant " 

2935 "against more detailed _local_remote_side " 

2936 "argument." 

2937 ) 

2938 

2939 remote_side = [r for (l, r) in self._local_remote_pairs] 

2940 else: 

2941 remote_side = self._remote_side 

2942 

2943 if self._refers_to_parent_table(): 

2944 self._annotate_selfref(lambda col: col in remote_side, True) 

2945 else: 

2946 

2947 def repl(element): 

2948 if element in remote_side: 

2949 return element._annotate({"remote": True}) 

2950 

2951 self.primaryjoin = visitors.replacement_traverse( 

2952 self.primaryjoin, {}, repl 

2953 ) 

2954 

2955 def _annotate_remote_with_overlap(self): 

2956 """annotate 'remote' in primaryjoin, secondaryjoin 

2957 when the parent/child tables have some set of 

2958 tables in common, though is not a fully self-referential 

2959 relationship. 

2960 

2961 """ 

2962 

2963 def visit_binary(binary): 

2964 binary.left, binary.right = proc_left_right( 

2965 binary.left, binary.right 

2966 ) 

2967 binary.right, binary.left = proc_left_right( 

2968 binary.right, binary.left 

2969 ) 

2970 

2971 check_entities = ( 

2972 self.prop is not None and self.prop.mapper is not self.prop.parent 

2973 ) 

2974 

2975 def proc_left_right(left, right): 

2976 if isinstance(left, expression.ColumnClause) and isinstance( 

2977 right, expression.ColumnClause 

2978 ): 

2979 if self.child_persist_selectable.c.contains_column( 

2980 right 

2981 ) and self.parent_persist_selectable.c.contains_column(left): 

2982 right = right._annotate({"remote": True}) 

2983 elif ( 

2984 check_entities 

2985 and right._annotations.get("parentmapper") is self.prop.mapper 

2986 ): 

2987 right = right._annotate({"remote": True}) 

2988 elif ( 

2989 check_entities 

2990 and left._annotations.get("parentmapper") is self.prop.mapper 

2991 ): 

2992 left = left._annotate({"remote": True}) 

2993 else: 

2994 self._warn_non_column_elements() 

2995 

2996 return left, right 

2997 

2998 self.primaryjoin = visitors.cloned_traverse( 

2999 self.primaryjoin, {}, {"binary": visit_binary} 

3000 ) 

3001 

3002 def _annotate_remote_distinct_selectables(self): 

3003 """annotate 'remote' in primaryjoin, secondaryjoin 

3004 when the parent/child tables are entirely 

3005 separate. 

3006 

3007 """ 

3008 

3009 def repl(element): 

3010 if self.child_persist_selectable.c.contains_column(element) and ( 

3011 not self.parent_local_selectable.c.contains_column(element) 

3012 or self.child_local_selectable.c.contains_column(element) 

3013 ): 

3014 return element._annotate({"remote": True}) 

3015 

3016 self.primaryjoin = visitors.replacement_traverse( 

3017 self.primaryjoin, {}, repl 

3018 ) 

3019 

3020 def _warn_non_column_elements(self): 

3021 util.warn( 

3022 "Non-simple column elements in primary " 

3023 "join condition for property %s - consider using " 

3024 "remote() annotations to mark the remote side." % self.prop 

3025 ) 

3026 

3027 def _annotate_local(self): 

3028 """Annotate the primaryjoin and secondaryjoin 

3029 structures with 'local' annotations. 

3030 

3031 This annotates all column elements found 

3032 simultaneously in the parent table 

3033 and the join condition that don't have a 

3034 'remote' annotation set up from 

3035 _annotate_remote() or user-defined. 

3036 

3037 """ 

3038 if self._has_annotation(self.primaryjoin, "local"): 

3039 return 

3040 

3041 if self._local_remote_pairs: 

3042 local_side = util.column_set( 

3043 [l for (l, r) in self._local_remote_pairs] 

3044 ) 

3045 else: 

3046 local_side = util.column_set(self.parent_persist_selectable.c) 

3047 

3048 def locals_(elem): 

3049 if "remote" not in elem._annotations and elem in local_side: 

3050 return elem._annotate({"local": True}) 

3051 

3052 self.primaryjoin = visitors.replacement_traverse( 

3053 self.primaryjoin, {}, locals_ 

3054 ) 

3055 

3056 def _annotate_parentmapper(self): 

3057 if self.prop is None: 

3058 return 

3059 

3060 def parentmappers_(elem): 

3061 if "remote" in elem._annotations: 

3062 return elem._annotate({"parentmapper": self.prop.mapper}) 

3063 elif "local" in elem._annotations: 

3064 return elem._annotate({"parentmapper": self.prop.parent}) 

3065 

3066 self.primaryjoin = visitors.replacement_traverse( 

3067 self.primaryjoin, {}, parentmappers_ 

3068 ) 

3069 

3070 def _check_remote_side(self): 

3071 if not self.local_remote_pairs: 

3072 raise sa_exc.ArgumentError( 

3073 "Relationship %s could " 

3074 "not determine any unambiguous local/remote column " 

3075 "pairs based on join condition and remote_side " 

3076 "arguments. " 

3077 "Consider using the remote() annotation to " 

3078 "accurately mark those elements of the join " 

3079 "condition that are on the remote side of " 

3080 "the relationship." % (self.prop,) 

3081 ) 

3082 

3083 def _check_foreign_cols(self, join_condition, primary): 

3084 """Check the foreign key columns collected and emit error 

3085 messages.""" 

3086 

3087 can_sync = False 

3088 

3089 foreign_cols = self._gather_columns_with_annotation( 

3090 join_condition, "foreign" 

3091 ) 

3092 

3093 has_foreign = bool(foreign_cols) 

3094 

3095 if primary: 

3096 can_sync = bool(self.synchronize_pairs) 

3097 else: 

3098 can_sync = bool(self.secondary_synchronize_pairs) 

3099 

3100 if ( 

3101 self.support_sync 

3102 and can_sync 

3103 or (not self.support_sync and has_foreign) 

3104 ): 

3105 return 

3106 

3107 # from here below is just determining the best error message 

3108 # to report. Check for a join condition using any operator 

3109 # (not just ==), perhaps they need to turn on "viewonly=True". 

3110 if self.support_sync and has_foreign and not can_sync: 

3111 err = ( 

3112 "Could not locate any simple equality expressions " 

3113 "involving locally mapped foreign key columns for " 

3114 "%s join condition " 

3115 "'%s' on relationship %s." 

3116 % ( 

3117 primary and "primary" or "secondary", 

3118 join_condition, 

3119 self.prop, 

3120 ) 

3121 ) 

3122 err += ( 

3123 " Ensure that referencing columns are associated " 

3124 "with a ForeignKey or ForeignKeyConstraint, or are " 

3125 "annotated in the join condition with the foreign() " 

3126 "annotation. To allow comparison operators other than " 

3127 "'==', the relationship can be marked as viewonly=True." 

3128 ) 

3129 

3130 raise sa_exc.ArgumentError(err) 

3131 else: 

3132 err = ( 

3133 "Could not locate any relevant foreign key columns " 

3134 "for %s join condition '%s' on relationship %s." 

3135 % ( 

3136 primary and "primary" or "secondary", 

3137 join_condition, 

3138 self.prop, 

3139 ) 

3140 ) 

3141 err += ( 

3142 " Ensure that referencing columns are associated " 

3143 "with a ForeignKey or ForeignKeyConstraint, or are " 

3144 "annotated in the join condition with the foreign() " 

3145 "annotation." 

3146 ) 

3147 raise sa_exc.ArgumentError(err) 

3148 

3149 def _determine_direction(self): 

3150 """Determine if this relationship is one to many, many to one, 

3151 many to many. 

3152 

3153 """ 

3154 if self.secondaryjoin is not None: 

3155 self.direction = MANYTOMANY 

3156 else: 

3157 parentcols = util.column_set(self.parent_persist_selectable.c) 

3158 targetcols = util.column_set(self.child_persist_selectable.c) 

3159 

3160 # fk collection which suggests ONETOMANY. 

3161 onetomany_fk = targetcols.intersection(self.foreign_key_columns) 

3162 

3163 # fk collection which suggests MANYTOONE. 

3164 

3165 manytoone_fk = parentcols.intersection(self.foreign_key_columns) 

3166 

3167 if onetomany_fk and manytoone_fk: 

3168 # fks on both sides. test for overlap of local/remote 

3169 # with foreign key. 

3170 # we will gather columns directly from their annotations 

3171 # without deannotating, so that we can distinguish on a column 

3172 # that refers to itself. 

3173 

3174 # 1. columns that are both remote and FK suggest 

3175 # onetomany. 

3176 onetomany_local = self._gather_columns_with_annotation( 

3177 self.primaryjoin, "remote", "foreign" 

3178 ) 

3179 

3180 # 2. columns that are FK but are not remote (e.g. local) 

3181 # suggest manytoone. 

3182 manytoone_local = set( 

3183 [ 

3184 c 

3185 for c in self._gather_columns_with_annotation( 

3186 self.primaryjoin, "foreign" 

3187 ) 

3188 if "remote" not in c._annotations 

3189 ] 

3190 ) 

3191 

3192 # 3. if both collections are present, remove columns that 

3193 # refer to themselves. This is for the case of 

3194 # and_(Me.id == Me.remote_id, Me.version == Me.version) 

3195 if onetomany_local and manytoone_local: 

3196 self_equated = self.remote_columns.intersection( 

3197 self.local_columns 

3198 ) 

3199 onetomany_local = onetomany_local.difference(self_equated) 

3200 manytoone_local = manytoone_local.difference(self_equated) 

3201 

3202 # at this point, if only one or the other collection is 

3203 # present, we know the direction, otherwise it's still 

3204 # ambiguous. 

3205 

3206 if onetomany_local and not manytoone_local: 

3207 self.direction = ONETOMANY 

3208 elif manytoone_local and not onetomany_local: 

3209 self.direction = MANYTOONE 

3210 else: 

3211 raise sa_exc.ArgumentError( 

3212 "Can't determine relationship" 

3213 " direction for relationship '%s' - foreign " 

3214 "key columns within the join condition are present " 

3215 "in both the parent and the child's mapped tables. " 

3216 "Ensure that only those columns referring " 

3217 "to a parent column are marked as foreign, " 

3218 "either via the foreign() annotation or " 

3219 "via the foreign_keys argument." % self.prop 

3220 ) 

3221 elif onetomany_fk: 

3222 self.direction = ONETOMANY 

3223 elif manytoone_fk: 

3224 self.direction = MANYTOONE 

3225 else: 

3226 raise sa_exc.ArgumentError( 

3227 "Can't determine relationship " 

3228 "direction for relationship '%s' - foreign " 

3229 "key columns are present in neither the parent " 

3230 "nor the child's mapped tables" % self.prop 

3231 ) 

3232 

3233 def _deannotate_pairs(self, collection): 

3234 """provide deannotation for the various lists of 

3235 pairs, so that using them in hashes doesn't incur 

3236 high-overhead __eq__() comparisons against 

3237 original columns mapped. 

3238 

3239 """ 

3240 return [(x._deannotate(), y._deannotate()) for x, y in collection] 

3241 

3242 def _setup_pairs(self): 

3243 sync_pairs = [] 

3244 lrp = util.OrderedSet([]) 

3245 secondary_sync_pairs = [] 

3246 

3247 def go(joincond, collection): 

3248 def visit_binary(binary, left, right): 

3249 if ( 

3250 "remote" in right._annotations 

3251 and "remote" not in left._annotations 

3252 and self.can_be_synced_fn(left) 

3253 ): 

3254 lrp.add((left, right)) 

3255 elif ( 

3256 "remote" in left._annotations 

3257 and "remote" not in right._annotations 

3258 and self.can_be_synced_fn(right) 

3259 ): 

3260 lrp.add((right, left)) 

3261 if binary.operator is operators.eq and self.can_be_synced_fn( 

3262 left, right 

3263 ): 

3264 if "foreign" in right._annotations: 

3265 collection.append((left, right)) 

3266 elif "foreign" in left._annotations: 

3267 collection.append((right, left)) 

3268 

3269 visit_binary_product(visit_binary, joincond) 

3270 

3271 for joincond, collection in [ 

3272 (self.primaryjoin, sync_pairs), 

3273 (self.secondaryjoin, secondary_sync_pairs), 

3274 ]: 

3275 if joincond is None: 

3276 continue 

3277 go(joincond, collection) 

3278 

3279 self.local_remote_pairs = self._deannotate_pairs(lrp) 

3280 self.synchronize_pairs = self._deannotate_pairs(sync_pairs) 

3281 self.secondary_synchronize_pairs = self._deannotate_pairs( 

3282 secondary_sync_pairs 

3283 ) 

3284 

3285 _track_overlapping_sync_targets = weakref.WeakKeyDictionary() 

3286 

3287 def _warn_for_conflicting_sync_targets(self): 

3288 if not self.support_sync: 

3289 return 

3290 

3291 # we would like to detect if we are synchronizing any column 

3292 # pairs in conflict with another relationship that wishes to sync 

3293 # an entirely different column to the same target. This is a 

3294 # very rare edge case so we will try to minimize the memory/overhead 

3295 # impact of this check 

3296 for from_, to_ in [ 

3297 (from_, to_) for (from_, to_) in self.synchronize_pairs 

3298 ] + [ 

3299 (from_, to_) for (from_, to_) in self.secondary_synchronize_pairs 

3300 ]: 

3301 # save ourselves a ton of memory and overhead by only 

3302 # considering columns that are subject to a overlapping 

3303 # FK constraints at the core level. This condition can arise 

3304 # if multiple relationships overlap foreign() directly, but 

3305 # we're going to assume it's typically a ForeignKeyConstraint- 

3306 # level configuration that benefits from this warning. 

3307 if len(to_.foreign_keys) < 2: 

3308 continue 

3309 

3310 if to_ not in self._track_overlapping_sync_targets: 

3311 self._track_overlapping_sync_targets[ 

3312 to_ 

3313 ] = weakref.WeakKeyDictionary({self.prop: from_}) 

3314 else: 

3315 other_props = [] 

3316 prop_to_from = self._track_overlapping_sync_targets[to_] 

3317 

3318 for pr, fr_ in prop_to_from.items(): 

3319 if ( 

3320 pr.mapper in mapperlib._mapper_registry 

3321 and ( 

3322 self.prop._persists_for(pr.parent) 

3323 or pr._persists_for(self.prop.parent) 

3324 ) 

3325 and fr_ is not from_ 

3326 and pr not in self.prop._reverse_property 

3327 ): 

3328 

3329 other_props.append((pr, fr_)) 

3330 

3331 if other_props: 

3332 util.warn( 

3333 "relationship '%s' will copy column %s to column %s, " 

3334 "which conflicts with relationship(s): %s. " 

3335 "Consider applying " 

3336 "viewonly=True to read-only relationships, or provide " 

3337 "a primaryjoin condition marking writable columns " 

3338 "with the foreign() annotation." 

3339 % ( 

3340 self.prop, 

3341 from_, 

3342 to_, 

3343 ", ".join( 

3344 "'%s' (copies %s to %s)" % (pr, fr_, to_) 

3345 for (pr, fr_) in other_props 

3346 ), 

3347 ) 

3348 ) 

3349 self._track_overlapping_sync_targets[to_][self.prop] = from_ 

3350 

3351 @util.memoized_property 

3352 def remote_columns(self): 

3353 return self._gather_join_annotations("remote") 

3354 

3355 @util.memoized_property 

3356 def local_columns(self): 

3357 return self._gather_join_annotations("local") 

3358 

3359 @util.memoized_property 

3360 def foreign_key_columns(self): 

3361 return self._gather_join_annotations("foreign") 

3362 

3363 def _gather_join_annotations(self, annotation): 

3364 s = set( 

3365 self._gather_columns_with_annotation(self.primaryjoin, annotation) 

3366 ) 

3367 if self.secondaryjoin is not None: 

3368 s.update( 

3369 self._gather_columns_with_annotation( 

3370 self.secondaryjoin, annotation 

3371 ) 

3372 ) 

3373 return {x._deannotate() for x in s} 

3374 

3375 def _gather_columns_with_annotation(self, clause, *annotation): 

3376 annotation = set(annotation) 

3377 return set( 

3378 [ 

3379 col 

3380 for col in visitors.iterate(clause, {}) 

3381 if annotation.issubset(col._annotations) 

3382 ] 

3383 ) 

3384 

3385 def join_targets( 

3386 self, source_selectable, dest_selectable, aliased, single_crit=None 

3387 ): 

3388 """Given a source and destination selectable, create a 

3389 join between them. 

3390 

3391 This takes into account aliasing the join clause 

3392 to reference the appropriate corresponding columns 

3393 in the target objects, as well as the extra child 

3394 criterion, equivalent column sets, etc. 

3395 

3396 """ 

3397 # place a barrier on the destination such that 

3398 # replacement traversals won't ever dig into it. 

3399 # its internal structure remains fixed 

3400 # regardless of context. 

3401 dest_selectable = _shallow_annotate( 

3402 dest_selectable, {"no_replacement_traverse": True} 

3403 ) 

3404 

3405 primaryjoin, secondaryjoin, secondary = ( 

3406 self.primaryjoin, 

3407 self.secondaryjoin, 

3408 self.secondary, 

3409 ) 

3410 

3411 # adjust the join condition for single table inheritance, 

3412 # in the case that the join is to a subclass 

3413 # this is analogous to the 

3414 # "_adjust_for_single_table_inheritance()" method in Query. 

3415 

3416 if single_crit is not None: 

3417 if secondaryjoin is not None: 

3418 secondaryjoin = secondaryjoin & single_crit 

3419 else: 

3420 primaryjoin = primaryjoin & single_crit 

3421 

3422 if aliased: 

3423 if secondary is not None: 

3424 secondary = secondary.alias(flat=True) 

3425 primary_aliasizer = ClauseAdapter( 

3426 secondary, exclude_fn=_ColInAnnotations("local") 

3427 ) 

3428 secondary_aliasizer = ClauseAdapter( 

3429 dest_selectable, equivalents=self.child_equivalents 

3430 ).chain(primary_aliasizer) 

3431 if source_selectable is not None: 

3432 primary_aliasizer = ClauseAdapter( 

3433 secondary, exclude_fn=_ColInAnnotations("local") 

3434 ).chain( 

3435 ClauseAdapter( 

3436 source_selectable, 

3437 equivalents=self.parent_equivalents, 

3438 ) 

3439 ) 

3440 

3441 secondaryjoin = secondary_aliasizer.traverse(secondaryjoin) 

3442 else: 

3443 primary_aliasizer = ClauseAdapter( 

3444 dest_selectable, 

3445 exclude_fn=_ColInAnnotations("local"), 

3446 equivalents=self.child_equivalents, 

3447 ) 

3448 if source_selectable is not None: 

3449 primary_aliasizer.chain( 

3450 ClauseAdapter( 

3451 source_selectable, 

3452 exclude_fn=_ColInAnnotations("remote"), 

3453 equivalents=self.parent_equivalents, 

3454 ) 

3455 ) 

3456 secondary_aliasizer = None 

3457 

3458 primaryjoin = primary_aliasizer.traverse(primaryjoin) 

3459 target_adapter = secondary_aliasizer or primary_aliasizer 

3460 target_adapter.exclude_fn = None 

3461 else: 

3462 target_adapter = None 

3463 return ( 

3464 primaryjoin, 

3465 secondaryjoin, 

3466 secondary, 

3467 target_adapter, 

3468 dest_selectable, 

3469 ) 

3470 

3471 def create_lazy_clause(self, reverse_direction=False): 

3472 binds = util.column_dict() 

3473 equated_columns = util.column_dict() 

3474 

3475 has_secondary = self.secondaryjoin is not None 

3476 

3477 if has_secondary: 

3478 lookup = collections.defaultdict(list) 

3479 for l, r in self.local_remote_pairs: 

3480 lookup[l].append((l, r)) 

3481 equated_columns[r] = l 

3482 elif not reverse_direction: 

3483 for l, r in self.local_remote_pairs: 

3484 equated_columns[r] = l 

3485 else: 

3486 for l, r in self.local_remote_pairs: 

3487 equated_columns[l] = r 

3488 

3489 def col_to_bind(col): 

3490 

3491 if ( 

3492 (not reverse_direction and "local" in col._annotations) 

3493 or reverse_direction 

3494 and ( 

3495 (has_secondary and col in lookup) 

3496 or (not has_secondary and "remote" in col._annotations) 

3497 ) 

3498 ): 

3499 if col not in binds: 

3500 binds[col] = sql.bindparam( 

3501 None, None, type_=col.type, unique=True 

3502 ) 

3503 return binds[col] 

3504 return None 

3505 

3506 lazywhere = self.primaryjoin 

3507 if self.secondaryjoin is None or not reverse_direction: 

3508 lazywhere = visitors.replacement_traverse( 

3509 lazywhere, {}, col_to_bind 

3510 ) 

3511 

3512 if self.secondaryjoin is not None: 

3513 secondaryjoin = self.secondaryjoin 

3514 if reverse_direction: 

3515 secondaryjoin = visitors.replacement_traverse( 

3516 secondaryjoin, {}, col_to_bind 

3517 ) 

3518 lazywhere = sql.and_(lazywhere, secondaryjoin) 

3519 

3520 bind_to_col = {binds[col].key: col for col in binds} 

3521 

3522 return lazywhere, bind_to_col, equated_columns 

3523 

3524 

3525class _ColInAnnotations(object): 

3526 """Seralizable equivalent to: 

3527 

3528 lambda c: "name" in c._annotations 

3529 """ 

3530 

3531 def __init__(self, name): 

3532 self.name = name 

3533 

3534 def __call__(self, c): 

3535 return self.name in c._annotations