Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/relationships.py: 24%

995 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1# orm/relationships.py 

2# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: https://www.opensource.org/licenses/mit-license.php 

7 

8"""Heuristics related to join conditions as used in 

9:func:`_orm.relationship`. 

10 

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

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

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

14 

15""" 

16from __future__ import absolute_import 

17 

18import collections 

19import re 

20import weakref 

21 

22from . import attributes 

23from .base import _is_mapped_class 

24from .base import PASSIVE_MERGE 

25from .base import state_str 

26from .interfaces import MANYTOMANY 

27from .interfaces import MANYTOONE 

28from .interfaces import ONETOMANY 

29from .interfaces import PropComparator 

30from .interfaces import StrategizedProperty 

31from .util import _orm_annotate 

32from .util import _orm_deannotate 

33from .util import CascadeOptions 

34from .. import exc as sa_exc 

35from .. import log 

36from .. import schema 

37from .. import sql 

38from .. import util 

39from ..inspection import inspect 

40from ..sql import coercions 

41from ..sql import expression 

42from ..sql import operators 

43from ..sql import roles 

44from ..sql import visitors 

45from ..sql.util import _deep_deannotate 

46from ..sql.util import _shallow_annotate 

47from ..sql.util import adapt_criterion_to_null 

48from ..sql.util import ClauseAdapter 

49from ..sql.util import join_condition 

50from ..sql.util import selectables_overlap 

51from ..sql.util import visit_binary_product 

52 

53 

54def remote(expr): 

55 """Annotate a portion of a primaryjoin expression 

56 with a 'remote' annotation. 

57 

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

59 description of use. 

60 

61 .. seealso:: 

62 

63 :ref:`relationship_custom_foreign` 

64 

65 :func:`.foreign` 

66 

67 """ 

68 return _annotate_columns( 

69 coercions.expect(roles.ColumnArgumentRole, expr), {"remote": True} 

70 ) 

71 

72 

73def foreign(expr): 

74 """Annotate a portion of a primaryjoin expression 

75 with a 'foreign' annotation. 

76 

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

78 description of use. 

79 

80 .. seealso:: 

81 

82 :ref:`relationship_custom_foreign` 

83 

84 :func:`.remote` 

85 

86 """ 

87 

88 return _annotate_columns( 

89 coercions.expect(roles.ColumnArgumentRole, expr), {"foreign": True} 

90 ) 

91 

92 

93@log.class_logger 

94class RelationshipProperty(StrategizedProperty): 

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

96 of items that correspond to a related database table. 

97 

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

99 

100 .. seealso:: 

101 

102 :ref:`relationship_config_toplevel` 

103 

104 """ 

105 

106 strategy_wildcard_key = "relationship" 

107 inherit_cache = True 

108 

109 _links_to_entity = True 

110 

111 _persistence_only = dict( 

112 passive_deletes=False, 

113 passive_updates=True, 

114 enable_typechecks=True, 

115 active_history=False, 

116 cascade_backrefs=True, 

117 ) 

118 

119 _dependency_processor = None 

120 

121 def __init__( 

122 self, 

123 argument, 

124 secondary=None, 

125 primaryjoin=None, 

126 secondaryjoin=None, 

127 foreign_keys=None, 

128 uselist=None, 

129 order_by=False, 

130 backref=None, 

131 back_populates=None, 

132 overlaps=None, 

133 post_update=False, 

134 cascade=False, 

135 viewonly=False, 

136 lazy="select", 

137 collection_class=None, 

138 passive_deletes=_persistence_only["passive_deletes"], 

139 passive_updates=_persistence_only["passive_updates"], 

140 remote_side=None, 

141 enable_typechecks=_persistence_only["enable_typechecks"], 

142 join_depth=None, 

143 comparator_factory=None, 

144 single_parent=False, 

145 innerjoin=False, 

146 distinct_target_key=None, 

147 doc=None, 

148 active_history=_persistence_only["active_history"], 

149 cascade_backrefs=_persistence_only["cascade_backrefs"], 

150 load_on_pending=False, 

151 bake_queries=True, 

152 _local_remote_pairs=None, 

153 query_class=None, 

154 info=None, 

155 omit_join=None, 

156 sync_backref=None, 

157 _legacy_inactive_history_style=False, 

158 ): 

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

160 

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

162 The constructed class is an instance of 

163 :class:`.RelationshipProperty`. 

164 

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

166 

167 mapper(Parent, properties={ 

168 'children': relationship(Child) 

169 }) 

170 

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

172 optionally accept a 

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

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

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

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

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

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

179 file:: 

180 

181 mapper(Parent, properties={ 

182 "children":relationship(lambda: Child, 

183 order_by=lambda: Child.id) 

184 }) 

185 

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

187 initializer allows string arguments to be passed to 

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

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

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

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

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

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

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

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

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

197 :func:`_orm.relationship` 

198 attempts to resolve the string reference to the 

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

200 follows:: 

201 

202 from sqlalchemy.ext.declarative import declarative_base 

203 

204 Base = declarative_base() 

205 

206 class Parent(Base): 

207 __tablename__ = 'parent' 

208 id = Column(Integer, primary_key=True) 

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

210 

211 .. seealso:: 

212 

213 :ref:`relationship_config_toplevel` - Full introductory and 

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

215 

216 :ref:`tutorial_orm_related_objects` - ORM tutorial introduction. 

217 

218 :param argument: 

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

220 representing 

221 the target of the relationship. 

222 

223 :paramref:`_orm.relationship.argument` 

224 may also be passed as a callable 

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

226 be passed as a string name when using Declarative. 

227 

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

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

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

231 See :ref:`declarative_relationship_eval` for details on 

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

233 

234 .. versionchanged 1.3.16:: 

235 

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

237 open ended Python expression, instead only accepting a string 

238 class name or dotted package-qualified name. 

239 

240 .. seealso:: 

241 

242 :ref:`declarative_configuring_relationships` - further detail 

243 on relationship configuration when using Declarative. 

244 

245 :param secondary: 

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

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

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

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

250 :class:`_expression.Join` construct. 

251 

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

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

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

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

256 that is 

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

258 collection associated with the 

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

260 

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

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

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

264 See :ref:`declarative_relationship_eval` for details on 

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

266 

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

268 typically applied in the case where the intermediary 

269 :class:`_schema.Table` 

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

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

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

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

274 :func:`_orm.relationship` 

275 is not used for persistence operations which 

276 may conflict with those of the association object pattern. 

277 

278 .. seealso:: 

279 

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

281 to many". 

282 

283 :ref:`self_referential_many_to_many` - Specifics on using 

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

285 

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

287 Declarative. 

288 

289 :ref:`association_pattern` - an alternative to 

290 :paramref:`_orm.relationship.secondary` 

291 when composing association 

292 table relationships, allowing additional attributes to be 

293 specified on the association table. 

294 

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

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

297 conditions to be used. 

298 

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

300 works 

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

302 instance. 

303 

304 :param active_history=False: 

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

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

307 not already loaded. Normally, history tracking logic for 

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

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

310 for applications that make use of 

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

312 the "previous" value of the attribute. 

313 

314 :param backref: 

315 A reference to a string relationship name, or a :func:`_orm.backref` 

316 construct, which will be used to automatically generate a new 

317 :func:`_orm.relationship` on the related class, which then refers to 

318 this one using a bi-directional 

319 :paramref:`_orm.relationship.back_populates` configuration. 

320 

321 In modern Python, explicit use of :func:`_orm.relationship` with 

322 :paramref:`_orm.relationship.back_populates` should be preferred, as 

323 it is more robust in terms of mapper configuration as well as more 

324 conceptually straightforward. It also integrates with new :pep:`484` 

325 typing features introduced in SQLAlchemy 2.0 which is not possible 

326 with dynamically generated attributes. 

327 

328 .. seealso:: 

329 

330 :ref:`relationships_backref` - notes on using 

331 :paramref:`_orm.relationship.backref` 

332 

333 :ref:`tutorial_orm_related_objects` - in the 

334 :ref:`unified_tutorial`, presents an overview of bi-directional 

335 relationship configuration and behaviors using 

336 :paramref:`_orm.relationship.back_populates` 

337 

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

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

340 

341 

342 :param back_populates: 

343 Indicates the name of a :func:`_orm.relationship` on the related 

344 class that will be synchronized with this one. It is usually 

345 expected that the :func:`_orm.relationship` on the related class 

346 also refer to this one. This allows objects on both sides of 

347 each :func:`_orm.relationship` to synchronize in-Python state 

348 changes and also provides directives to the :term:`unit of work` 

349 flush process how changes along these relationships should 

350 be persisted. 

351 

352 .. seealso:: 

353 

354 :ref:`tutorial_orm_related_objects` - in the 

355 :ref:`unified_tutorial`, presents an overview of bi-directional 

356 relationship configuration and behaviors. 

357 

358 :ref:`relationship_patterns` - includes many examples of 

359 :paramref:`_orm.relationship.back_populates`. 

360 

361 :param overlaps: 

362 A string name or comma-delimited set of names of other relationships 

363 on either this mapper, a descendant mapper, or a target mapper with 

364 which this relationship may write to the same foreign keys upon 

365 persistence. The only effect this has is to eliminate the 

366 warning that this relationship will conflict with another upon 

367 persistence. This is used for such relationships that are truly 

368 capable of conflicting with each other on write, but the application 

369 will ensure that no such conflicts occur. 

370 

371 .. versionadded:: 1.4 

372 

373 .. seealso:: 

374 

375 :ref:`error_qzyx` - usage example 

376 

377 :param bake_queries=True: 

378 Legacy parameter, not used. 

379 

380 .. versionchanged:: 1.4.23 the "lambda caching" system is no longer 

381 used by loader strategies and the ``bake_queries`` parameter 

382 has no effect. 

383 

384 :param cascade: 

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

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

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

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

389 

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

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

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

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

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

395 to indicate that related objects should follow along with the 

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

397 

398 .. seealso:: 

399 

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

401 cascade options. 

402 

403 :param cascade_backrefs=True: 

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

405 operate along an assignment event intercepted by a backref. 

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

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

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

409 

410 .. deprecated:: 1.4 The 

411 :paramref:`_orm.relationship.cascade_backrefs` 

412 flag will default to False in all cases in SQLAlchemy 2.0. 

413 

414 .. seealso:: 

415 

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

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

418 

419 :param collection_class: 

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

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

422 

423 .. seealso:: 

424 

425 :ref:`custom_collections` - Introductory documentation and 

426 examples. 

427 

428 :param comparator_factory: 

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

430 which provides custom SQL clause generation for comparison 

431 operations. 

432 

433 .. seealso:: 

434 

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

436 at this level. 

437 

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

439 

440 

441 :param distinct_target_key=None: 

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

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

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

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

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

447 innermost SELECT unconditionally. 

448 

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

450 reducing performance of the innermost subquery beyond that of what 

451 duplicate innermost rows may be causing. 

452 

453 .. versionchanged:: 0.9.0 - 

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

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

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

457 key. 

458 

459 .. seealso:: 

460 

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

462 eager loading. 

463 

464 :param doc: 

465 Docstring which will be applied to the resulting descriptor. 

466 

467 :param foreign_keys: 

468 

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

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

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

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

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

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

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

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

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

478 

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

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

481 automatically determine which columns in the 

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

483 considered "foreign key" columns based on those 

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

485 :class:`_schema.ForeignKey`, 

486 or are otherwise listed as referencing columns in a 

487 :class:`_schema.ForeignKeyConstraint` construct. 

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

489 

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

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

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

493 :func:`_orm.relationship` 

494 to consider just those columns specified 

495 here as "foreign". 

496 

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

498 :class:`_schema.ForeignKey` or 

499 :class:`_schema.ForeignKeyConstraint` 

500 constructs present, often because the table 

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

502 reflection (MySQL MyISAM). 

503 

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

505 argument is used to 

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

507 columns or expressions that do not normally refer to their 

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

509 complex comparison using a SQL function. 

510 

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

512 error messages that suggest the use of the 

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

514 presented with an ambiguous condition. In typical cases, 

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

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

517 not needed. 

518 

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

520 callable function which is evaluated at mapper initialization time, 

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

522 Declarative. 

523 

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

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

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

527 See :ref:`declarative_relationship_eval` for details on 

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

529 

530 .. seealso:: 

531 

532 :ref:`relationship_foreign_keys` 

533 

534 :ref:`relationship_custom_foreign` 

535 

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

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

538 condition. 

539 

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

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

542 

543 :param innerjoin=False: 

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

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

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

547 generally perform better than outer joins. 

548 

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

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

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

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

553 

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

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

556 for details on nested / unnested behaviors. 

557 

558 .. seealso:: 

559 

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

561 loader option, including detail on nesting behavior. 

562 

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

564 various loader options. 

565 

566 

567 :param join_depth: 

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

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

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

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

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

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

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

575 both to joined- and subquery- eager loaders. 

576 

577 .. seealso:: 

578 

579 :ref:`self_referential_eager_loading` - Introductory documentation 

580 and examples. 

581 

582 :param lazy='select': specifies 

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

584 ``select``. Values include: 

585 

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

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

588 fetch for simple many-to-one references. 

589 

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

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

592 simple many-to-one references. 

593 

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

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

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

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

598 

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

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

601 a subquery of the original statement, for each collection 

602 requested. 

603 

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

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

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

607 key identifiers using an IN clause. 

608 

609 .. versionadded:: 1.2 

610 

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

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

613 populated in some manner specific to the application. 

614 

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

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

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

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

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

620 

621 .. versionadded:: 1.1 

622 

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

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

625 eager loading, will raise an 

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

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

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

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

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

631 additional SELECT statements should be blocked. 

632 

633 .. versionadded:: 1.1 

634 

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

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

637 operations, onto which further filtering operations can be 

638 applied before iterating the results. See 

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

640 

641 * True - a synonym for 'select' 

642 

643 * False - a synonym for 'joined' 

644 

645 * None - a synonym for 'noload' 

646 

647 .. seealso:: 

648 

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

650 relationship loader configuration. 

651 

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

653 

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

655 

656 :param load_on_pending=False: 

657 Indicates loading behavior for transient or pending parent objects. 

658 

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

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

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

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

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

664 collection. 

665 

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

667 flag does not improve 

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

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

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

671 This flag is not not intended for general use. 

672 

673 .. seealso:: 

674 

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

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

677 also allows loading on objects that remain transient or 

678 detached. 

679 

680 :param order_by: 

681 Indicates the ordering that should be applied when loading these 

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

683 is expected to refer to 

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

685 objects to which the target class is 

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

687 refers to the column. 

688 

689 :paramref:`_orm.relationship.order_by` 

690 may also be passed as a callable 

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

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

693 

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

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

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

697 See :ref:`declarative_relationship_eval` for details on 

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

699 

700 :param passive_deletes=False: 

701 Indicates loading behavior during delete operations. 

702 

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

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

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

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

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

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

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

710 database side. 

711 

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

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

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

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

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

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

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

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

720 object is de-associated with the parent. 

721 

722 .. seealso:: 

723 

724 :ref:`passive_deletes` - Introductory documentation 

725 and examples. 

726 

727 :param passive_updates=True: 

728 Indicates the persistence behavior to take when a referenced 

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

730 foreign key columns will also need their value changed. 

731 

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

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

734 handle propagation of an UPDATE from a source column to 

735 dependent rows. When False, the SQLAlchemy 

736 :func:`_orm.relationship` 

737 construct will attempt to emit its own UPDATE statements to 

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

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

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

741 the database is in fact enforcing referential integrity, unless 

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

743 supports it. 

744 

745 It is highly advised that an application which is employing 

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

747 and instead uses the referential integrity features of the database 

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

749 

750 .. seealso:: 

751 

752 :ref:`passive_updates` - Introductory documentation and 

753 examples. 

754 

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

756 takes effect for joined-table inheritance mappings. 

757 

758 :param post_update: 

759 This indicates that the relationship should be handled by a 

760 second UPDATE statement after an INSERT or before a 

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

762 instance was UPDATEd as well, although this technically should 

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

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

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

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

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

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

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

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

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

772 a flush operation returns an error that a "cyclical 

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

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

775 

776 .. seealso:: 

777 

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

779 

780 :param primaryjoin: 

781 A SQL expression that will be used as the primary 

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

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

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

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

786 association table). 

787 

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

789 callable function which is evaluated at mapper initialization time, 

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

791 Declarative. 

792 

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

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

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

796 See :ref:`declarative_relationship_eval` for details on 

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

798 

799 .. seealso:: 

800 

801 :ref:`relationship_primaryjoin` 

802 

803 :param remote_side: 

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

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

806 

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

808 callable function which is evaluated at mapper initialization time, 

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

810 Declarative. 

811 

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

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

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

815 See :ref:`declarative_relationship_eval` for details on 

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

817 

818 .. seealso:: 

819 

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

821 :paramref:`_orm.relationship.remote_side` 

822 is used to configure self-referential relationships. 

823 

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

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

826 typically 

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

828 is used. 

829 

830 :param query_class: 

831 A :class:`_query.Query` 

832 subclass that will be used internally by the 

833 ``AppenderQuery`` returned by a "dynamic" relationship, that 

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

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

836 function. 

837 

838 .. seealso:: 

839 

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

841 relationship loaders. 

842 

843 :param secondaryjoin: 

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

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

846 computed based on the foreign key relationships of the association 

847 and child tables. 

848 

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

850 callable function which is evaluated at mapper initialization time, 

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

852 Declarative. 

853 

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

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

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

857 See :ref:`declarative_relationship_eval` for details on 

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

859 

860 .. seealso:: 

861 

862 :ref:`relationship_primaryjoin` 

863 

864 :param single_parent: 

865 When True, installs a validator which will prevent objects 

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

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

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

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

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

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

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

873 instructing when this option is required. 

874 

875 .. seealso:: 

876 

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

878 :paramref:`_orm.relationship.single_parent` 

879 flag may be appropriate. 

880 

881 :param uselist: 

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

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

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

885 time, based on the type and direction 

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

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

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

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

890 to 

891 False. 

892 

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

894 flag is also available on an 

895 existing :func:`_orm.relationship` 

896 construct as a read-only attribute, 

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

898 deals 

899 with collections or scalar attributes:: 

900 

901 >>> User.addresses.property.uselist 

902 True 

903 

904 .. seealso:: 

905 

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

907 one" relationship pattern, which is typically when the 

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

909 

910 :param viewonly=False: 

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

912 objects, and not for any persistence operation. A 

913 :func:`_orm.relationship` which specifies 

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

915 with a wider range of SQL operations within the 

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

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

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

919 :paramref:`_orm.relationship.viewonly` 

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

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

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

923 collection from resulting in persistence operations. 

924 

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

926 conjunction with backrefs, the originating relationship for a 

927 particular state change will not produce state changes within the 

928 viewonly relationship. This is the behavior implied by 

929 :paramref:`_orm.relationship.sync_backref` being set to False. 

930 

931 .. versionchanged:: 1.3.17 - the 

932 :paramref:`_orm.relationship.sync_backref` flag is set to False 

933 when using viewonly in conjunction with backrefs. 

934 

935 .. seealso:: 

936 

937 :paramref:`_orm.relationship.sync_backref` 

938 

939 :param sync_backref: 

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

941 attributes when this relationship is target of either 

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

943 :paramref:`_orm.relationship.back_populates`. 

944 

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

946 be selected based on the value of the 

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

948 default, changes in state will be back-populated only if neither 

949 sides of a relationship is viewonly. 

950 

951 .. versionadded:: 1.3.17 

952 

953 .. versionchanged:: 1.4 - A relationship that specifies 

954 :paramref:`_orm.relationship.viewonly` automatically implies 

955 that :paramref:`_orm.relationship.sync_backref` is ``False``. 

956 

957 .. seealso:: 

958 

959 :paramref:`_orm.relationship.viewonly` 

960 

961 :param omit_join: 

962 Allows manual control over the "selectin" automatic join 

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

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

965 optimization in place. 

966 

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

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

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

970 optimization is not supported. 

971 

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

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

974 

975 .. versionadded:: 1.3 

976 

977 

978 """ 

979 super(RelationshipProperty, self).__init__() 

980 

981 self.uselist = uselist 

982 self.argument = argument 

983 self.secondary = secondary 

984 self.primaryjoin = primaryjoin 

985 self.secondaryjoin = secondaryjoin 

986 self.post_update = post_update 

987 self.direction = None 

988 self.viewonly = viewonly 

989 if viewonly: 

990 self._warn_for_persistence_only_flags( 

991 passive_deletes=passive_deletes, 

992 passive_updates=passive_updates, 

993 enable_typechecks=enable_typechecks, 

994 active_history=active_history, 

995 cascade_backrefs=cascade_backrefs, 

996 ) 

997 if viewonly and sync_backref: 

998 raise sa_exc.ArgumentError( 

999 "sync_backref and viewonly cannot both be True" 

1000 ) 

1001 self.sync_backref = sync_backref 

1002 self.lazy = lazy 

1003 self.single_parent = single_parent 

1004 self._user_defined_foreign_keys = foreign_keys 

1005 self.collection_class = collection_class 

1006 self.passive_deletes = passive_deletes 

1007 self.cascade_backrefs = cascade_backrefs 

1008 self.passive_updates = passive_updates 

1009 self.remote_side = remote_side 

1010 self.enable_typechecks = enable_typechecks 

1011 self.query_class = query_class 

1012 self.innerjoin = innerjoin 

1013 self.distinct_target_key = distinct_target_key 

1014 self.doc = doc 

1015 self.active_history = active_history 

1016 self._legacy_inactive_history_style = _legacy_inactive_history_style 

1017 

1018 self.join_depth = join_depth 

1019 if omit_join: 

1020 util.warn( 

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

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

1023 "flag is set explicitly. omit_join optimization is " 

1024 "automatically detected for conditions under which it is " 

1025 "supported." 

1026 ) 

1027 

1028 self.omit_join = omit_join 

1029 self.local_remote_pairs = _local_remote_pairs 

1030 self.bake_queries = bake_queries 

1031 self.load_on_pending = load_on_pending 

1032 self.comparator_factory = ( 

1033 comparator_factory or RelationshipProperty.Comparator 

1034 ) 

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

1036 util.set_creation_order(self) 

1037 

1038 if info is not None: 

1039 self.info = info 

1040 

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

1042 

1043 self._reverse_property = set() 

1044 if overlaps: 

1045 self._overlaps = set(re.split(r"\s*,\s*", overlaps)) 

1046 else: 

1047 self._overlaps = () 

1048 

1049 if cascade is not False: 

1050 self.cascade = cascade 

1051 elif self.viewonly: 

1052 self.cascade = "merge" 

1053 else: 

1054 self.cascade = "save-update, merge" 

1055 

1056 self.order_by = order_by 

1057 

1058 self.back_populates = back_populates 

1059 

1060 if self.back_populates: 

1061 if backref: 

1062 raise sa_exc.ArgumentError( 

1063 "backref and back_populates keyword arguments " 

1064 "are mutually exclusive" 

1065 ) 

1066 self.backref = None 

1067 else: 

1068 self.backref = backref 

1069 

1070 def _warn_for_persistence_only_flags(self, **kw): 

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

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

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

1074 # configuration mistake, and Python shows regular warnings more 

1075 # aggressively than deprecation warnings by default. Unlike the 

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

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

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

1079 # raise an error. 

1080 util.warn( 

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

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

1083 "viewonly=True relationship does not perform persistence " 

1084 "operations. This configuration may raise an error " 

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

1086 ) 

1087 

1088 def instrument_class(self, mapper): 

1089 attributes.register_descriptor( 

1090 mapper.class_, 

1091 self.key, 

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

1093 parententity=mapper, 

1094 doc=self.doc, 

1095 ) 

1096 

1097 class Comparator(PropComparator): 

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

1099 :class:`.RelationshipProperty` attributes. 

1100 

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

1102 overview of ORM level operator definition. 

1103 

1104 .. seealso:: 

1105 

1106 :class:`.PropComparator` 

1107 

1108 :class:`.ColumnProperty.Comparator` 

1109 

1110 :class:`.ColumnOperators` 

1111 

1112 :ref:`types_operators` 

1113 

1114 :attr:`.TypeEngine.comparator_factory` 

1115 

1116 """ 

1117 

1118 _of_type = None 

1119 _extra_criteria = () 

1120 

1121 def __init__( 

1122 self, 

1123 prop, 

1124 parentmapper, 

1125 adapt_to_entity=None, 

1126 of_type=None, 

1127 extra_criteria=(), 

1128 ): 

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

1130 is internal to the ORM's attribute mechanics. 

1131 

1132 """ 

1133 self.prop = prop 

1134 self._parententity = parentmapper 

1135 self._adapt_to_entity = adapt_to_entity 

1136 if of_type: 

1137 self._of_type = of_type 

1138 self._extra_criteria = extra_criteria 

1139 

1140 def adapt_to_entity(self, adapt_to_entity): 

1141 return self.__class__( 

1142 self.property, 

1143 self._parententity, 

1144 adapt_to_entity=adapt_to_entity, 

1145 of_type=self._of_type, 

1146 ) 

1147 

1148 @util.memoized_property 

1149 def entity(self): 

1150 """The target entity referred to by this 

1151 :class:`.RelationshipProperty.Comparator`. 

1152 

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

1154 object. 

1155 

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

1157 :func:`_orm.relationship`. 

1158 

1159 """ 

1160 # this is a relatively recent change made for 

1161 # 1.4.27 as part of #7244. 

1162 # TODO: shouldn't _of_type be inspected up front when received? 

1163 if self._of_type is not None: 

1164 return inspect(self._of_type) 

1165 else: 

1166 return self.property.entity 

1167 

1168 @util.memoized_property 

1169 def mapper(self): 

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

1171 :class:`.RelationshipProperty.Comparator`. 

1172 

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

1174 :func:`_orm.relationship`. 

1175 

1176 """ 

1177 return self.property.mapper 

1178 

1179 @util.memoized_property 

1180 def _parententity(self): 

1181 return self.property.parent 

1182 

1183 def _source_selectable(self): 

1184 if self._adapt_to_entity: 

1185 return self._adapt_to_entity.selectable 

1186 else: 

1187 return self.property.parent._with_polymorphic_selectable 

1188 

1189 def __clause_element__(self): 

1190 adapt_from = self._source_selectable() 

1191 if self._of_type: 

1192 of_type_entity = inspect(self._of_type) 

1193 else: 

1194 of_type_entity = None 

1195 

1196 ( 

1197 pj, 

1198 sj, 

1199 source, 

1200 dest, 

1201 secondary, 

1202 target_adapter, 

1203 ) = self.property._create_joins( 

1204 source_selectable=adapt_from, 

1205 source_polymorphic=True, 

1206 of_type_entity=of_type_entity, 

1207 alias_secondary=True, 

1208 extra_criteria=self._extra_criteria, 

1209 ) 

1210 if sj is not None: 

1211 return pj & sj 

1212 else: 

1213 return pj 

1214 

1215 def of_type(self, cls): 

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

1217 

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

1219 

1220 

1221 """ 

1222 return RelationshipProperty.Comparator( 

1223 self.property, 

1224 self._parententity, 

1225 adapt_to_entity=self._adapt_to_entity, 

1226 of_type=cls, 

1227 extra_criteria=self._extra_criteria, 

1228 ) 

1229 

1230 def and_(self, *other): 

1231 """Add AND criteria. 

1232 

1233 See :meth:`.PropComparator.and_` for an example. 

1234 

1235 .. versionadded:: 1.4 

1236 

1237 """ 

1238 return RelationshipProperty.Comparator( 

1239 self.property, 

1240 self._parententity, 

1241 adapt_to_entity=self._adapt_to_entity, 

1242 of_type=self._of_type, 

1243 extra_criteria=self._extra_criteria + other, 

1244 ) 

1245 

1246 def in_(self, other): 

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

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

1249 

1250 """ 

1251 raise NotImplementedError( 

1252 "in_() not yet supported for " 

1253 "relationships. For a simple " 

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

1255 "the set of foreign key values." 

1256 ) 

1257 

1258 __hash__ = None 

1259 

1260 def __eq__(self, other): 

1261 """Implement the ``==`` operator. 

1262 

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

1264 

1265 MyClass.some_prop == <some object> 

1266 

1267 this will typically produce a 

1268 clause such as:: 

1269 

1270 mytable.related_id == <some id> 

1271 

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

1273 object. 

1274 

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

1276 many-to-one comparisons: 

1277 

1278 * Comparisons against collections are not supported. 

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

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

1281 clause that compares the target columns in the parent to 

1282 the given target. 

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

1284 of the association table will be rendered as 

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

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

1287 queries that go beyond simple AND conjunctions of 

1288 comparisons, such as those which use OR. Use 

1289 explicit joins, outerjoins, or 

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

1291 more comprehensive non-many-to-one scalar 

1292 membership tests. 

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

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

1295 

1296 """ 

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

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

1299 return ~self._criterion_exists() 

1300 else: 

1301 return _orm_annotate( 

1302 self.property._optimized_compare( 

1303 None, adapt_source=self.adapter 

1304 ) 

1305 ) 

1306 elif self.property.uselist: 

1307 raise sa_exc.InvalidRequestError( 

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

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

1310 ) 

1311 else: 

1312 return _orm_annotate( 

1313 self.property._optimized_compare( 

1314 other, adapt_source=self.adapter 

1315 ) 

1316 ) 

1317 

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

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

1320 info = inspect(self._of_type) 

1321 target_mapper, to_selectable, is_aliased_class = ( 

1322 info.mapper, 

1323 info.selectable, 

1324 info.is_aliased_class, 

1325 ) 

1326 if self.property._is_self_referential and not is_aliased_class: 

1327 to_selectable = to_selectable._anonymous_fromclause() 

1328 

1329 single_crit = target_mapper._single_table_criterion 

1330 if single_crit is not None: 

1331 if criterion is not None: 

1332 criterion = single_crit & criterion 

1333 else: 

1334 criterion = single_crit 

1335 else: 

1336 is_aliased_class = False 

1337 to_selectable = None 

1338 

1339 if self.adapter: 

1340 source_selectable = self._source_selectable() 

1341 else: 

1342 source_selectable = None 

1343 

1344 ( 

1345 pj, 

1346 sj, 

1347 source, 

1348 dest, 

1349 secondary, 

1350 target_adapter, 

1351 ) = self.property._create_joins( 

1352 dest_selectable=to_selectable, 

1353 source_selectable=source_selectable, 

1354 ) 

1355 

1356 for k in kwargs: 

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

1358 if criterion is None: 

1359 criterion = crit 

1360 else: 

1361 criterion = criterion & crit 

1362 

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

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

1365 # pj its the local side of the primaryjoin. 

1366 if sj is not None: 

1367 j = _orm_annotate(pj) & sj 

1368 else: 

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

1370 

1371 if ( 

1372 criterion is not None 

1373 and target_adapter 

1374 and not is_aliased_class 

1375 ): 

1376 # limit this adapter to annotated only? 

1377 criterion = target_adapter.traverse(criterion) 

1378 

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

1380 # return be subject to Query adaption. The right 

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

1382 # should not correlate or otherwise reach out 

1383 # to anything in the enclosing query. 

1384 if criterion is not None: 

1385 criterion = criterion._annotate( 

1386 {"no_replacement_traverse": True} 

1387 ) 

1388 

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

1390 

1391 if secondary is not None: 

1392 ex = ( 

1393 sql.exists(1) 

1394 .where(crit) 

1395 .select_from(dest, secondary) 

1396 .correlate_except(dest, secondary) 

1397 ) 

1398 else: 

1399 ex = ( 

1400 sql.exists(1) 

1401 .where(crit) 

1402 .select_from(dest) 

1403 .correlate_except(dest) 

1404 ) 

1405 return ex 

1406 

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

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

1409 particular criterion, using EXISTS. 

1410 

1411 An expression like:: 

1412 

1413 session.query(MyClass).filter( 

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

1415 ) 

1416 

1417 

1418 Will produce a query like:: 

1419 

1420 SELECT * FROM my_table WHERE 

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

1422 AND related.x=2) 

1423 

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

1425 a correlated subquery, its performance is not nearly as 

1426 good when compared against large target tables as that of 

1427 using a join. 

1428 

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

1430 useful for testing for empty collections:: 

1431 

1432 session.query(MyClass).filter( 

1433 ~MyClass.somereference.any() 

1434 ) 

1435 

1436 will produce:: 

1437 

1438 SELECT * FROM my_table WHERE 

1439 NOT (EXISTS (SELECT 1 FROM related WHERE 

1440 related.my_id=my_table.id)) 

1441 

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

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

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

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

1446 

1447 """ 

1448 if not self.property.uselist: 

1449 raise sa_exc.InvalidRequestError( 

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

1451 "attributes. Use has()." 

1452 ) 

1453 

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

1455 

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

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

1458 particular criterion, using EXISTS. 

1459 

1460 An expression like:: 

1461 

1462 session.query(MyClass).filter( 

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

1464 ) 

1465 

1466 

1467 Will produce a query like:: 

1468 

1469 SELECT * FROM my_table WHERE 

1470 EXISTS (SELECT 1 FROM related WHERE 

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

1472 

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

1474 a correlated subquery, its performance is not nearly as 

1475 good when compared against large target tables as that of 

1476 using a join. 

1477 

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

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

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

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

1482 

1483 """ 

1484 if self.property.uselist: 

1485 raise sa_exc.InvalidRequestError( 

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

1487 ) 

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

1489 

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

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

1492 containment of a particular item. 

1493 

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

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

1496 :func:`_orm.relationship` that implements 

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

1498 

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

1500 expression like:: 

1501 

1502 MyClass.contains(other) 

1503 

1504 Produces a clause like:: 

1505 

1506 mytable.id == <some id> 

1507 

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

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

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

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

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

1513 operations. 

1514 

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

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

1517 has more caveats. The association table will be 

1518 rendered in the statement, producing an "implicit" 

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

1520 clause which are equated in the WHERE clause:: 

1521 

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

1523 

1524 Produces a query like:: 

1525 

1526 SELECT * FROM my_table, my_association_table AS 

1527 my_association_table_1 WHERE 

1528 my_table.id = my_association_table_1.parent_id 

1529 AND my_association_table_1.child_id = <some id> 

1530 

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

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

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

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

1535 used in queries that move beyond simple AND 

1536 conjunctions, such as multiple 

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

1538 expressions joined by OR. In such cases subqueries or 

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

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

1541 a less-performant alternative using EXISTS, or refer 

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

1543 as well as :ref:`orm_queryguide_joins` 

1544 for more details on constructing outer joins. 

1545 

1546 kwargs may be ignored by this operator but are required for API 

1547 conformance. 

1548 """ 

1549 if not self.property.uselist: 

1550 raise sa_exc.InvalidRequestError( 

1551 "'contains' not implemented for scalar " 

1552 "attributes. Use ==" 

1553 ) 

1554 clause = self.property._optimized_compare( 

1555 other, adapt_source=self.adapter 

1556 ) 

1557 

1558 if self.property.secondaryjoin is not None: 

1559 clause.negation_clause = self.__negated_contains_or_equals( 

1560 other 

1561 ) 

1562 

1563 return clause 

1564 

1565 def __negated_contains_or_equals(self, other): 

1566 if self.property.direction == MANYTOONE: 

1567 state = attributes.instance_state(other) 

1568 

1569 def state_bindparam(local_col, state, remote_col): 

1570 dict_ = state.dict 

1571 return sql.bindparam( 

1572 local_col.key, 

1573 type_=local_col.type, 

1574 unique=True, 

1575 callable_=self.property._get_attr_w_warn_on_none( 

1576 self.property.mapper, state, dict_, remote_col 

1577 ), 

1578 ) 

1579 

1580 def adapt(col): 

1581 if self.adapter: 

1582 return self.adapter(col) 

1583 else: 

1584 return col 

1585 

1586 if self.property._use_get: 

1587 return sql.and_( 

1588 *[ 

1589 sql.or_( 

1590 adapt(x) 

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

1592 adapt(x) == None, 

1593 ) 

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

1595 ] 

1596 ) 

1597 

1598 criterion = sql.and_( 

1599 *[ 

1600 x == y 

1601 for (x, y) in zip( 

1602 self.property.mapper.primary_key, 

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

1604 ) 

1605 ] 

1606 ) 

1607 

1608 return ~self._criterion_exists(criterion) 

1609 

1610 def __ne__(self, other): 

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

1612 

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

1614 

1615 MyClass.some_prop != <some object> 

1616 

1617 This will typically produce a clause such as:: 

1618 

1619 mytable.related_id != <some id> 

1620 

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

1622 given object. 

1623 

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

1625 many-to-one comparisons: 

1626 

1627 * Comparisons against collections are not supported. 

1628 Use 

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

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

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

1632 clause that compares the target columns in the parent to 

1633 the given target. 

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

1635 of the association table will be rendered as 

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

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

1638 queries that go beyond simple AND conjunctions of 

1639 comparisons, such as those which use OR. Use 

1640 explicit joins, outerjoins, or 

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

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

1643 more comprehensive non-many-to-one scalar 

1644 membership tests. 

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

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

1647 

1648 """ 

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

1650 if self.property.direction == MANYTOONE: 

1651 return _orm_annotate( 

1652 ~self.property._optimized_compare( 

1653 None, adapt_source=self.adapter 

1654 ) 

1655 ) 

1656 

1657 else: 

1658 return self._criterion_exists() 

1659 elif self.property.uselist: 

1660 raise sa_exc.InvalidRequestError( 

1661 "Can't compare a collection" 

1662 " to an object or collection; use " 

1663 "contains() to test for membership." 

1664 ) 

1665 else: 

1666 return _orm_annotate(self.__negated_contains_or_equals(other)) 

1667 

1668 @util.memoized_property 

1669 def property(self): 

1670 self.prop.parent._check_configure() 

1671 return self.prop 

1672 

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

1674 assert instance is not None 

1675 adapt_source = None 

1676 if from_entity is not None: 

1677 insp = inspect(from_entity) 

1678 if insp.is_aliased_class: 

1679 adapt_source = insp._adapter.adapt_clause 

1680 return self._optimized_compare( 

1681 instance, 

1682 value_is_parent=True, 

1683 adapt_source=adapt_source, 

1684 alias_secondary=alias_secondary, 

1685 ) 

1686 

1687 def _optimized_compare( 

1688 self, 

1689 state, 

1690 value_is_parent=False, 

1691 adapt_source=None, 

1692 alias_secondary=True, 

1693 ): 

1694 if state is not None: 

1695 try: 

1696 state = inspect(state) 

1697 except sa_exc.NoInspectionAvailable: 

1698 state = None 

1699 

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

1701 raise sa_exc.ArgumentError( 

1702 "Mapped instance expected for relationship " 

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

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

1705 "comparison with a subquery, " 

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

1707 ) 

1708 reverse_direction = not value_is_parent 

1709 

1710 if state is None: 

1711 return self._lazy_none_clause( 

1712 reverse_direction, adapt_source=adapt_source 

1713 ) 

1714 

1715 if not reverse_direction: 

1716 criterion, bind_to_col = ( 

1717 self._lazy_strategy._lazywhere, 

1718 self._lazy_strategy._bind_to_col, 

1719 ) 

1720 else: 

1721 criterion, bind_to_col = ( 

1722 self._lazy_strategy._rev_lazywhere, 

1723 self._lazy_strategy._rev_bind_to_col, 

1724 ) 

1725 

1726 if reverse_direction: 

1727 mapper = self.mapper 

1728 else: 

1729 mapper = self.parent 

1730 

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

1732 

1733 def visit_bindparam(bindparam): 

1734 if bindparam._identifying_key in bind_to_col: 

1735 bindparam.callable = self._get_attr_w_warn_on_none( 

1736 mapper, 

1737 state, 

1738 dict_, 

1739 bind_to_col[bindparam._identifying_key], 

1740 ) 

1741 

1742 if self.secondary is not None and alias_secondary: 

1743 criterion = ClauseAdapter( 

1744 self.secondary._anonymous_fromclause() 

1745 ).traverse(criterion) 

1746 

1747 criterion = visitors.cloned_traverse( 

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

1749 ) 

1750 

1751 if adapt_source: 

1752 criterion = adapt_source(criterion) 

1753 return criterion 

1754 

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

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

1757 

1758 E.g.:: 

1759 

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

1761 

1762 expr = Address.user == u1 

1763 

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

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

1766 of ``u1``. 

1767 

1768 """ 

1769 

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

1771 # a wide variety of scenarios, including: 

1772 # 

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

1774 # the attribute as of yet 

1775 # 

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

1777 # value 

1778 # 

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

1780 # 

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

1782 # and the object is also detached 

1783 # 

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

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

1786 # they're trying to evaluate it 

1787 # 

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

1789 # then expired 

1790 # 

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

1792 # then expired, then the object was detached 

1793 # 

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

1795 # the comparison correctly for that so warn 

1796 # 

1797 

1798 prop = mapper.get_property_by_column(column) 

1799 

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

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

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

1803 state._track_last_known_value(prop.key) 

1804 

1805 def _go(): 

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

1807 existing_is_available = last_known is not attributes.NO_VALUE 

1808 

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

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

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

1812 # the last known value 

1813 current_value = mapper._get_state_attr_by_column( 

1814 state, 

1815 dict_, 

1816 column, 

1817 passive=attributes.PASSIVE_OFF 

1818 if state.persistent 

1819 else attributes.PASSIVE_NO_FETCH ^ attributes.INIT_OK, 

1820 ) 

1821 

1822 if current_value is attributes.NEVER_SET: 

1823 if not existing_is_available: 

1824 raise sa_exc.InvalidRequestError( 

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

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

1827 % (column, state_str(state)) 

1828 ) 

1829 elif current_value is attributes.PASSIVE_NO_RESULT: 

1830 if not existing_is_available: 

1831 raise sa_exc.InvalidRequestError( 

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

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

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

1835 ) 

1836 else: 

1837 to_return = current_value 

1838 if to_return is None: 

1839 util.warn( 

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

1841 "for a relationship comparison and will not " 

1842 "currently produce an IS comparison " 

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

1844 ) 

1845 return to_return 

1846 

1847 return _go 

1848 

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

1850 if not reverse_direction: 

1851 criterion, bind_to_col = ( 

1852 self._lazy_strategy._lazywhere, 

1853 self._lazy_strategy._bind_to_col, 

1854 ) 

1855 else: 

1856 criterion, bind_to_col = ( 

1857 self._lazy_strategy._rev_lazywhere, 

1858 self._lazy_strategy._rev_bind_to_col, 

1859 ) 

1860 

1861 criterion = adapt_criterion_to_null(criterion, bind_to_col) 

1862 

1863 if adapt_source: 

1864 criterion = adapt_source(criterion) 

1865 return criterion 

1866 

1867 def __str__(self): 

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

1869 

1870 def merge( 

1871 self, 

1872 session, 

1873 source_state, 

1874 source_dict, 

1875 dest_state, 

1876 dest_dict, 

1877 load, 

1878 _recursive, 

1879 _resolve_conflict_map, 

1880 ): 

1881 

1882 if load: 

1883 for r in self._reverse_property: 

1884 if (source_state, r) in _recursive: 

1885 return 

1886 

1887 if "merge" not in self._cascade: 

1888 return 

1889 

1890 if self.key not in source_dict: 

1891 return 

1892 

1893 if self.uselist: 

1894 impl = source_state.get_impl(self.key) 

1895 instances_iterable = impl.get_collection(source_state, source_dict) 

1896 

1897 # if this is a CollectionAttributeImpl, then empty should 

1898 # be False, otherwise "self.key in source_dict" should not be 

1899 # True 

1900 assert not instances_iterable.empty if impl.collection else True 

1901 

1902 if load: 

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

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

1905 # map for those already present. 

1906 # also assumes CollectionAttributeImpl behavior of loading 

1907 # "old" list in any case 

1908 dest_state.get_impl(self.key).get( 

1909 dest_state, dest_dict, passive=PASSIVE_MERGE 

1910 ) 

1911 

1912 dest_list = [] 

1913 for current in instances_iterable: 

1914 current_state = attributes.instance_state(current) 

1915 current_dict = attributes.instance_dict(current) 

1916 _recursive[(current_state, self)] = True 

1917 obj = session._merge( 

1918 current_state, 

1919 current_dict, 

1920 load=load, 

1921 _recursive=_recursive, 

1922 _resolve_conflict_map=_resolve_conflict_map, 

1923 ) 

1924 if obj is not None: 

1925 dest_list.append(obj) 

1926 

1927 if not load: 

1928 coll = attributes.init_state_collection( 

1929 dest_state, dest_dict, self.key 

1930 ) 

1931 for c in dest_list: 

1932 coll.append_without_event(c) 

1933 else: 

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

1935 dest_state, 

1936 dest_dict, 

1937 dest_list, 

1938 _adapt=False, 

1939 passive=PASSIVE_MERGE, 

1940 ) 

1941 else: 

1942 current = source_dict[self.key] 

1943 if current is not None: 

1944 current_state = attributes.instance_state(current) 

1945 current_dict = attributes.instance_dict(current) 

1946 _recursive[(current_state, self)] = True 

1947 obj = session._merge( 

1948 current_state, 

1949 current_dict, 

1950 load=load, 

1951 _recursive=_recursive, 

1952 _resolve_conflict_map=_resolve_conflict_map, 

1953 ) 

1954 else: 

1955 obj = None 

1956 

1957 if not load: 

1958 dest_dict[self.key] = obj 

1959 else: 

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

1961 dest_state, dest_dict, obj, None 

1962 ) 

1963 

1964 def _value_as_iterable( 

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

1966 ): 

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

1968 key. 

1969 

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

1971 """ 

1972 

1973 impl = state.manager[key].impl 

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

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

1976 return [] 

1977 elif hasattr(impl, "get_collection"): 

1978 return [ 

1979 (attributes.instance_state(o), o) 

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

1981 ] 

1982 else: 

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

1984 

1985 def cascade_iterator( 

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

1987 ): 

1988 # assert type_ in self._cascade 

1989 

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

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

1992 passive = attributes.PASSIVE_NO_INITIALIZE 

1993 else: 

1994 passive = attributes.PASSIVE_OFF 

1995 

1996 if type_ == "save-update": 

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

1998 

1999 else: 

2000 tuples = self._value_as_iterable( 

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

2002 ) 

2003 

2004 skip_pending = ( 

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

2006 ) 

2007 

2008 for instance_state, c in tuples: 

2009 if instance_state in visited_states: 

2010 continue 

2011 

2012 if c is None: 

2013 # would like to emit a warning here, but 

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

2015 # current behavior of silently skipping. 

2016 # see [ticket:2229] 

2017 continue 

2018 

2019 instance_dict = attributes.instance_dict(c) 

2020 

2021 if halt_on and halt_on(instance_state): 

2022 continue 

2023 

2024 if skip_pending and not instance_state.key: 

2025 continue 

2026 

2027 instance_mapper = instance_state.manager.mapper 

2028 

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

2030 raise AssertionError( 

2031 "Attribute '%s' on class '%s' " 

2032 "doesn't handle objects " 

2033 "of type '%s'" 

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

2035 ) 

2036 

2037 visited_states.add(instance_state) 

2038 

2039 yield c, instance_mapper, instance_state, instance_dict 

2040 

2041 @property 

2042 def _effective_sync_backref(self): 

2043 if self.viewonly: 

2044 return False 

2045 else: 

2046 return self.sync_backref is not False 

2047 

2048 @staticmethod 

2049 def _check_sync_backref(rel_a, rel_b): 

2050 if rel_a.viewonly and rel_b.sync_backref: 

2051 raise sa_exc.InvalidRequestError( 

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

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

2054 ) 

2055 if ( 

2056 rel_a.viewonly 

2057 and not rel_b.viewonly 

2058 and rel_b.sync_backref is not False 

2059 ): 

2060 rel_b.sync_backref = False 

2061 

2062 def _add_reverse_property(self, key): 

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

2064 if not isinstance(other, RelationshipProperty): 

2065 raise sa_exc.InvalidRequestError( 

2066 "back_populates on relationship '%s' refers to attribute '%s' " 

2067 "that is not a relationship. The back_populates parameter " 

2068 "should refer to the name of a relationship on the target " 

2069 "class." % (self, other) 

2070 ) 

2071 # viewonly and sync_backref cases 

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

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

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

2075 self._check_sync_backref(self, other) 

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

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

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

2079 self._check_sync_backref(other, self) 

2080 

2081 self._reverse_property.add(other) 

2082 other._reverse_property.add(self) 

2083 

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

2085 raise sa_exc.ArgumentError( 

2086 "reverse_property %r on " 

2087 "relationship %s references relationship %s, which " 

2088 "does not reference mapper %s" 

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

2090 ) 

2091 

2092 if ( 

2093 self.direction in (ONETOMANY, MANYTOONE) 

2094 and self.direction == other.direction 

2095 ): 

2096 raise sa_exc.ArgumentError( 

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

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

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

2100 % (other, self, self.direction) 

2101 ) 

2102 

2103 @util.memoized_property 

2104 @util.preload_module("sqlalchemy.orm.mapper") 

2105 def entity(self): 

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

2107 class or aliased class that is referred towards. 

2108 

2109 """ 

2110 

2111 mapperlib = util.preloaded.orm_mapper 

2112 

2113 if isinstance(self.argument, util.string_types): 

2114 argument = self._clsregistry_resolve_name(self.argument)() 

2115 

2116 elif callable(self.argument) and not isinstance( 

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

2118 ): 

2119 argument = self.argument() 

2120 else: 

2121 argument = self.argument 

2122 

2123 if isinstance(argument, type): 

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

2125 

2126 try: 

2127 entity = inspect(argument) 

2128 except sa_exc.NoInspectionAvailable: 

2129 pass 

2130 else: 

2131 if hasattr(entity, "mapper"): 

2132 return entity 

2133 

2134 raise sa_exc.ArgumentError( 

2135 "relationship '%s' expects " 

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

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

2138 ) 

2139 

2140 @util.memoized_property 

2141 def mapper(self): 

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

2143 :class:`.RelationshipProperty`. 

2144 

2145 This is a lazy-initializing static attribute. 

2146 

2147 """ 

2148 return self.entity.mapper 

2149 

2150 def do_init(self): 

2151 self._check_conflicts() 

2152 self._process_dependent_arguments() 

2153 self._setup_registry_dependencies() 

2154 self._setup_join_conditions() 

2155 self._check_cascade_settings(self._cascade) 

2156 self._post_init() 

2157 self._generate_backref() 

2158 self._join_condition._warn_for_conflicting_sync_targets() 

2159 super(RelationshipProperty, self).do_init() 

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

2161 

2162 def _setup_registry_dependencies(self): 

2163 self.parent.mapper.registry._set_depends_on( 

2164 self.entity.mapper.registry 

2165 ) 

2166 

2167 def _process_dependent_arguments(self): 

2168 """Convert incoming configuration arguments to their 

2169 proper form. 

2170 

2171 Callables are resolved, ORM annotations removed. 

2172 

2173 """ 

2174 

2175 # accept callables for other attributes which may require 

2176 # deferred initialization. This technique is used 

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

2178 for attr in ( 

2179 "order_by", 

2180 "primaryjoin", 

2181 "secondaryjoin", 

2182 "secondary", 

2183 "_user_defined_foreign_keys", 

2184 "remote_side", 

2185 ): 

2186 attr_value = getattr(self, attr) 

2187 

2188 if isinstance(attr_value, util.string_types): 

2189 setattr( 

2190 self, 

2191 attr, 

2192 self._clsregistry_resolve_arg( 

2193 attr_value, favor_tables=attr == "secondary" 

2194 )(), 

2195 ) 

2196 elif callable(attr_value) and not _is_mapped_class(attr_value): 

2197 setattr(self, attr, attr_value()) 

2198 

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

2200 # descriptors are used to create the join expression. 

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

2202 val = getattr(self, attr) 

2203 if val is not None: 

2204 setattr( 

2205 self, 

2206 attr, 

2207 _orm_deannotate( 

2208 coercions.expect( 

2209 roles.ColumnArgumentRole, val, argname=attr 

2210 ) 

2211 ), 

2212 ) 

2213 

2214 if self.secondary is not None and _is_mapped_class(self.secondary): 

2215 raise sa_exc.ArgumentError( 

2216 "secondary argument %s passed to to relationship() %s must " 

2217 "be a Table object or other FROM clause; can't send a mapped " 

2218 "class directly as rows in 'secondary' are persisted " 

2219 "independently of a class that is mapped " 

2220 "to that same table." % (self.secondary, self) 

2221 ) 

2222 

2223 # ensure expressions in self.order_by, foreign_keys, 

2224 # remote_side are all columns, not strings. 

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

2226 self.order_by = tuple( 

2227 coercions.expect( 

2228 roles.ColumnArgumentRole, x, argname="order_by" 

2229 ) 

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

2231 ) 

2232 

2233 self._user_defined_foreign_keys = util.column_set( 

2234 coercions.expect( 

2235 roles.ColumnArgumentRole, x, argname="foreign_keys" 

2236 ) 

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

2238 ) 

2239 

2240 self.remote_side = util.column_set( 

2241 coercions.expect( 

2242 roles.ColumnArgumentRole, x, argname="remote_side" 

2243 ) 

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

2245 ) 

2246 

2247 self.target = self.entity.persist_selectable 

2248 

2249 def _setup_join_conditions(self): 

2250 self._join_condition = jc = JoinCondition( 

2251 parent_persist_selectable=self.parent.persist_selectable, 

2252 child_persist_selectable=self.entity.persist_selectable, 

2253 parent_local_selectable=self.parent.local_table, 

2254 child_local_selectable=self.entity.local_table, 

2255 primaryjoin=self.primaryjoin, 

2256 secondary=self.secondary, 

2257 secondaryjoin=self.secondaryjoin, 

2258 parent_equivalents=self.parent._equivalent_columns, 

2259 child_equivalents=self.mapper._equivalent_columns, 

2260 consider_as_foreign_keys=self._user_defined_foreign_keys, 

2261 local_remote_pairs=self.local_remote_pairs, 

2262 remote_side=self.remote_side, 

2263 self_referential=self._is_self_referential, 

2264 prop=self, 

2265 support_sync=not self.viewonly, 

2266 can_be_synced_fn=self._columns_are_mapped, 

2267 ) 

2268 self.primaryjoin = jc.primaryjoin 

2269 self.secondaryjoin = jc.secondaryjoin 

2270 self.direction = jc.direction 

2271 self.local_remote_pairs = jc.local_remote_pairs 

2272 self.remote_side = jc.remote_columns 

2273 self.local_columns = jc.local_columns 

2274 self.synchronize_pairs = jc.synchronize_pairs 

2275 self._calculated_foreign_keys = jc.foreign_key_columns 

2276 self.secondary_synchronize_pairs = jc.secondary_synchronize_pairs 

2277 

2278 @property 

2279 def _clsregistry_resolve_arg(self): 

2280 return self._clsregistry_resolvers[1] 

2281 

2282 @property 

2283 def _clsregistry_resolve_name(self): 

2284 return self._clsregistry_resolvers[0] 

2285 

2286 @util.memoized_property 

2287 @util.preload_module("sqlalchemy.orm.clsregistry") 

2288 def _clsregistry_resolvers(self): 

2289 _resolver = util.preloaded.orm_clsregistry._resolver 

2290 

2291 return _resolver(self.parent.class_, self) 

2292 

2293 @util.preload_module("sqlalchemy.orm.mapper") 

2294 def _check_conflicts(self): 

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

2296 inheritance conflicts.""" 

2297 mapperlib = util.preloaded.orm_mapper 

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

2299 self.parent.class_, configure=False 

2300 ).has_property(self.key): 

2301 raise sa_exc.ArgumentError( 

2302 "Attempting to assign a new " 

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

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

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

2306 "created for class '%s' " 

2307 % ( 

2308 self.key, 

2309 self.parent.class_.__name__, 

2310 self.parent.class_.__name__, 

2311 ) 

2312 ) 

2313 

2314 @property 

2315 def cascade(self): 

2316 """Return the current cascade setting for this 

2317 :class:`.RelationshipProperty`. 

2318 """ 

2319 return self._cascade 

2320 

2321 @cascade.setter 

2322 def cascade(self, cascade): 

2323 self._set_cascade(cascade) 

2324 

2325 def _set_cascade(self, cascade): 

2326 cascade = CascadeOptions(cascade) 

2327 

2328 if self.viewonly: 

2329 non_viewonly = set(cascade).difference( 

2330 CascadeOptions._viewonly_cascades 

2331 ) 

2332 if non_viewonly: 

2333 raise sa_exc.ArgumentError( 

2334 'Cascade settings "%s" apply to persistence operations ' 

2335 "and should not be combined with a viewonly=True " 

2336 "relationship." % (", ".join(sorted(non_viewonly))) 

2337 ) 

2338 

2339 if "mapper" in self.__dict__: 

2340 self._check_cascade_settings(cascade) 

2341 self._cascade = cascade 

2342 

2343 if self._dependency_processor: 

2344 self._dependency_processor.cascade = cascade 

2345 

2346 def _check_cascade_settings(self, cascade): 

2347 if ( 

2348 cascade.delete_orphan 

2349 and not self.single_parent 

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

2351 ): 

2352 raise sa_exc.ArgumentError( 

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

2354 "cascade is normally " 

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

2356 "relationship, " 

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

2358 "relationship. " 

2359 "To force this relationship to allow a particular " 

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

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

2362 "%(rel)s relationship, which " 

2363 "would allow " 

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

2365 "the single_parent=True flag." 

2366 % { 

2367 "rel": self, 

2368 "direction": "many-to-one" 

2369 if self.direction is MANYTOONE 

2370 else "many-to-many", 

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

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

2373 }, 

2374 code="bbf0", 

2375 ) 

2376 

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

2378 "delete" in cascade or "delete-orphan" in cascade 

2379 ): 

2380 raise sa_exc.ArgumentError( 

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

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

2383 ) 

2384 

2385 if cascade.delete_orphan: 

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

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

2388 ) 

2389 

2390 def _persists_for(self, mapper): 

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

2392 of the given mapper. 

2393 

2394 """ 

2395 

2396 return ( 

2397 self.key in mapper.relationships 

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

2399 ) 

2400 

2401 def _columns_are_mapped(self, *cols): 

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

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

2404 

2405 """ 

2406 for c in cols: 

2407 if ( 

2408 self.secondary is not None 

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

2410 ): 

2411 continue 

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

2413 c 

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

2415 return False 

2416 return True 

2417 

2418 def _generate_backref(self): 

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

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

2421 

2422 if self.parent.non_primary: 

2423 return 

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

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

2426 backref_key, kwargs = self.backref, {} 

2427 else: 

2428 backref_key, kwargs = self.backref 

2429 mapper = self.mapper.primary_mapper() 

2430 

2431 if not mapper.concrete: 

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

2433 mapper.self_and_descendants 

2434 ) 

2435 for m in check: 

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

2437 raise sa_exc.ArgumentError( 

2438 "Error creating backref " 

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

2440 "name exists on mapper '%s'" 

2441 % (backref_key, self, m) 

2442 ) 

2443 

2444 # determine primaryjoin/secondaryjoin for the 

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

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

2447 # both directions. 

2448 if self.secondary is not None: 

2449 # for many to many, just switch primaryjoin/ 

2450 # secondaryjoin. use the annotated 

2451 # pj/sj on the _join_condition. 

2452 pj = kwargs.pop( 

2453 "primaryjoin", 

2454 self._join_condition.secondaryjoin_minus_local, 

2455 ) 

2456 sj = kwargs.pop( 

2457 "secondaryjoin", 

2458 self._join_condition.primaryjoin_minus_local, 

2459 ) 

2460 else: 

2461 pj = kwargs.pop( 

2462 "primaryjoin", 

2463 self._join_condition.primaryjoin_reverse_remote, 

2464 ) 

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

2466 if sj: 

2467 raise sa_exc.InvalidRequestError( 

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

2469 "against a non-secondary relationship." 

2470 ) 

2471 

2472 foreign_keys = kwargs.pop( 

2473 "foreign_keys", self._user_defined_foreign_keys 

2474 ) 

2475 parent = self.parent.primary_mapper() 

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

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

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

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

2480 self.back_populates = backref_key 

2481 relationship = RelationshipProperty( 

2482 parent, 

2483 self.secondary, 

2484 pj, 

2485 sj, 

2486 foreign_keys=foreign_keys, 

2487 back_populates=self.key, 

2488 **kwargs 

2489 ) 

2490 mapper._configure_property(backref_key, relationship) 

2491 

2492 if self.back_populates: 

2493 self._add_reverse_property(self.back_populates) 

2494 

2495 @util.preload_module("sqlalchemy.orm.dependency") 

2496 def _post_init(self): 

2497 dependency = util.preloaded.orm_dependency 

2498 

2499 if self.uselist is None: 

2500 self.uselist = self.direction is not MANYTOONE 

2501 if not self.viewonly: 

2502 self._dependency_processor = ( 

2503 dependency.DependencyProcessor.from_relationship 

2504 )(self) 

2505 

2506 @util.memoized_property 

2507 def _use_get(self): 

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

2509 lazyloader.""" 

2510 

2511 strategy = self._lazy_strategy 

2512 return strategy.use_get 

2513 

2514 @util.memoized_property 

2515 def _is_self_referential(self): 

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

2517 

2518 def _create_joins( 

2519 self, 

2520 source_polymorphic=False, 

2521 source_selectable=None, 

2522 dest_selectable=None, 

2523 of_type_entity=None, 

2524 alias_secondary=False, 

2525 extra_criteria=(), 

2526 ): 

2527 

2528 aliased = False 

2529 

2530 if alias_secondary and self.secondary is not None: 

2531 aliased = True 

2532 

2533 if source_selectable is None: 

2534 if source_polymorphic and self.parent.with_polymorphic: 

2535 source_selectable = self.parent._with_polymorphic_selectable 

2536 

2537 if of_type_entity: 

2538 dest_mapper = of_type_entity.mapper 

2539 if dest_selectable is None: 

2540 dest_selectable = of_type_entity.selectable 

2541 aliased = True 

2542 else: 

2543 dest_mapper = self.mapper 

2544 

2545 if dest_selectable is None: 

2546 dest_selectable = self.entity.selectable 

2547 if self.mapper.with_polymorphic: 

2548 aliased = True 

2549 

2550 if self._is_self_referential and source_selectable is None: 

2551 dest_selectable = dest_selectable._anonymous_fromclause() 

2552 aliased = True 

2553 elif ( 

2554 dest_selectable is not self.mapper._with_polymorphic_selectable 

2555 or self.mapper.with_polymorphic 

2556 ): 

2557 aliased = True 

2558 

2559 single_crit = dest_mapper._single_table_criterion 

2560 aliased = aliased or ( 

2561 source_selectable is not None 

2562 and ( 

2563 source_selectable 

2564 is not self.parent._with_polymorphic_selectable 

2565 or source_selectable._is_subquery 

2566 ) 

2567 ) 

2568 

2569 ( 

2570 primaryjoin, 

2571 secondaryjoin, 

2572 secondary, 

2573 target_adapter, 

2574 dest_selectable, 

2575 ) = self._join_condition.join_targets( 

2576 source_selectable, 

2577 dest_selectable, 

2578 aliased, 

2579 single_crit, 

2580 extra_criteria, 

2581 ) 

2582 if source_selectable is None: 

2583 source_selectable = self.parent.local_table 

2584 if dest_selectable is None: 

2585 dest_selectable = self.entity.local_table 

2586 return ( 

2587 primaryjoin, 

2588 secondaryjoin, 

2589 source_selectable, 

2590 dest_selectable, 

2591 secondary, 

2592 target_adapter, 

2593 ) 

2594 

2595 

2596def _annotate_columns(element, annotations): 

2597 def clone(elem): 

2598 if isinstance(elem, expression.ColumnClause): 

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

2600 elem._copy_internals(clone=clone) 

2601 return elem 

2602 

2603 if element is not None: 

2604 element = clone(element) 

2605 clone = None # remove gc cycles 

2606 return element 

2607 

2608 

2609class JoinCondition(object): 

2610 def __init__( 

2611 self, 

2612 parent_persist_selectable, 

2613 child_persist_selectable, 

2614 parent_local_selectable, 

2615 child_local_selectable, 

2616 primaryjoin=None, 

2617 secondary=None, 

2618 secondaryjoin=None, 

2619 parent_equivalents=None, 

2620 child_equivalents=None, 

2621 consider_as_foreign_keys=None, 

2622 local_remote_pairs=None, 

2623 remote_side=None, 

2624 self_referential=False, 

2625 prop=None, 

2626 support_sync=True, 

2627 can_be_synced_fn=lambda *c: True, 

2628 ): 

2629 self.parent_persist_selectable = parent_persist_selectable 

2630 self.parent_local_selectable = parent_local_selectable 

2631 self.child_persist_selectable = child_persist_selectable 

2632 self.child_local_selectable = child_local_selectable 

2633 self.parent_equivalents = parent_equivalents 

2634 self.child_equivalents = child_equivalents 

2635 self.primaryjoin = primaryjoin 

2636 self.secondaryjoin = secondaryjoin 

2637 self.secondary = secondary 

2638 self.consider_as_foreign_keys = consider_as_foreign_keys 

2639 self._local_remote_pairs = local_remote_pairs 

2640 self._remote_side = remote_side 

2641 self.prop = prop 

2642 self.self_referential = self_referential 

2643 self.support_sync = support_sync 

2644 self.can_be_synced_fn = can_be_synced_fn 

2645 self._determine_joins() 

2646 self._sanitize_joins() 

2647 self._annotate_fks() 

2648 self._annotate_remote() 

2649 self._annotate_local() 

2650 self._annotate_parentmapper() 

2651 self._setup_pairs() 

2652 self._check_foreign_cols(self.primaryjoin, True) 

2653 if self.secondaryjoin is not None: 

2654 self._check_foreign_cols(self.secondaryjoin, False) 

2655 self._determine_direction() 

2656 self._check_remote_side() 

2657 self._log_joins() 

2658 

2659 def _log_joins(self): 

2660 if self.prop is None: 

2661 return 

2662 log = self.prop.logger 

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

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

2665 log.info( 

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

2667 self.prop, 

2668 ",".join( 

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

2670 ), 

2671 ) 

2672 log.info( 

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

2674 self.prop, 

2675 ",".join( 

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

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

2678 ), 

2679 ) 

2680 log.info( 

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

2682 self.prop, 

2683 ",".join( 

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

2685 ), 

2686 ) 

2687 log.info( 

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

2689 self.prop, 

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

2691 ) 

2692 log.info( 

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

2694 self.prop, 

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

2696 ) 

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

2698 

2699 def _sanitize_joins(self): 

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

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

2702 

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

2704 an exotic use case in _join_fixture_inh_selfref_w_entity 

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

2706 

2707 """ 

2708 

2709 self.primaryjoin = _deep_deannotate( 

2710 self.primaryjoin, values=("parententity", "proxy_key") 

2711 ) 

2712 if self.secondaryjoin is not None: 

2713 self.secondaryjoin = _deep_deannotate( 

2714 self.secondaryjoin, values=("parententity", "proxy_key") 

2715 ) 

2716 

2717 def _determine_joins(self): 

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

2719 if not passed to the constructor already. 

2720 

2721 This is based on analysis of the foreign key relationships 

2722 between the parent and target mapped selectables. 

2723 

2724 """ 

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

2726 raise sa_exc.ArgumentError( 

2727 "Property %s specified with secondary " 

2728 "join condition but " 

2729 "no secondary argument" % self.prop 

2730 ) 

2731 

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

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

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

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

2736 # a join. 

2737 try: 

2738 consider_as_foreign_keys = self.consider_as_foreign_keys or None 

2739 if self.secondary is not None: 

2740 if self.secondaryjoin is None: 

2741 self.secondaryjoin = join_condition( 

2742 self.child_persist_selectable, 

2743 self.secondary, 

2744 a_subset=self.child_local_selectable, 

2745 consider_as_foreign_keys=consider_as_foreign_keys, 

2746 ) 

2747 if self.primaryjoin is None: 

2748 self.primaryjoin = join_condition( 

2749 self.parent_persist_selectable, 

2750 self.secondary, 

2751 a_subset=self.parent_local_selectable, 

2752 consider_as_foreign_keys=consider_as_foreign_keys, 

2753 ) 

2754 else: 

2755 if self.primaryjoin is None: 

2756 self.primaryjoin = join_condition( 

2757 self.parent_persist_selectable, 

2758 self.child_persist_selectable, 

2759 a_subset=self.parent_local_selectable, 

2760 consider_as_foreign_keys=consider_as_foreign_keys, 

2761 ) 

2762 except sa_exc.NoForeignKeysError as nfe: 

2763 if self.secondary is not None: 

2764 util.raise_( 

2765 sa_exc.NoForeignKeysError( 

2766 "Could not determine join " 

2767 "condition between parent/child tables on " 

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

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

2770 "Ensure that referencing columns are associated " 

2771 "with a ForeignKey or ForeignKeyConstraint, or " 

2772 "specify 'primaryjoin' and 'secondaryjoin' " 

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

2774 ), 

2775 from_=nfe, 

2776 ) 

2777 else: 

2778 util.raise_( 

2779 sa_exc.NoForeignKeysError( 

2780 "Could not determine join " 

2781 "condition between parent/child tables on " 

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

2783 "linking these tables. " 

2784 "Ensure that referencing columns are associated " 

2785 "with a ForeignKey or ForeignKeyConstraint, or " 

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

2787 ), 

2788 from_=nfe, 

2789 ) 

2790 except sa_exc.AmbiguousForeignKeysError as afe: 

2791 if self.secondary is not None: 

2792 util.raise_( 

2793 sa_exc.AmbiguousForeignKeysError( 

2794 "Could not determine join " 

2795 "condition between parent/child tables on " 

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

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

2798 "Specify the 'foreign_keys' " 

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

2800 "should be counted as containing a foreign key " 

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

2802 "parent and child tables." 

2803 % (self.prop, self.secondary) 

2804 ), 

2805 from_=afe, 

2806 ) 

2807 else: 

2808 util.raise_( 

2809 sa_exc.AmbiguousForeignKeysError( 

2810 "Could not determine join " 

2811 "condition between parent/child tables on " 

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

2813 "paths linking the tables. Specify the " 

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

2815 "columns which should be counted as containing a " 

2816 "foreign key reference to the parent table." 

2817 % self.prop 

2818 ), 

2819 from_=afe, 

2820 ) 

2821 

2822 @property 

2823 def primaryjoin_minus_local(self): 

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

2825 

2826 @property 

2827 def secondaryjoin_minus_local(self): 

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

2829 

2830 @util.memoized_property 

2831 def primaryjoin_reverse_remote(self): 

2832 """Return the primaryjoin condition suitable for the 

2833 "reverse" direction. 

2834 

2835 If the primaryjoin was delivered here with pre-existing 

2836 "remote" annotations, the local/remote annotations 

2837 are reversed. Otherwise, the local/remote annotations 

2838 are removed. 

2839 

2840 """ 

2841 if self._has_remote_annotations: 

2842 

2843 def replace(element): 

2844 if "remote" in element._annotations: 

2845 v = dict(element._annotations) 

2846 del v["remote"] 

2847 v["local"] = True 

2848 return element._with_annotations(v) 

2849 elif "local" in element._annotations: 

2850 v = dict(element._annotations) 

2851 del v["local"] 

2852 v["remote"] = True 

2853 return element._with_annotations(v) 

2854 

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

2856 else: 

2857 if self._has_foreign_annotations: 

2858 # TODO: coverage 

2859 return _deep_deannotate( 

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

2861 ) 

2862 else: 

2863 return _deep_deannotate(self.primaryjoin) 

2864 

2865 def _has_annotation(self, clause, annotation): 

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

2867 if annotation in col._annotations: 

2868 return True 

2869 else: 

2870 return False 

2871 

2872 @util.memoized_property 

2873 def _has_foreign_annotations(self): 

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

2875 

2876 @util.memoized_property 

2877 def _has_remote_annotations(self): 

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

2879 

2880 def _annotate_fks(self): 

2881 """Annotate the primaryjoin and secondaryjoin 

2882 structures with 'foreign' annotations marking columns 

2883 considered as foreign. 

2884 

2885 """ 

2886 if self._has_foreign_annotations: 

2887 return 

2888 

2889 if self.consider_as_foreign_keys: 

2890 self._annotate_from_fk_list() 

2891 else: 

2892 self._annotate_present_fks() 

2893 

2894 def _annotate_from_fk_list(self): 

2895 def check_fk(col): 

2896 if col in self.consider_as_foreign_keys: 

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

2898 

2899 self.primaryjoin = visitors.replacement_traverse( 

2900 self.primaryjoin, {}, check_fk 

2901 ) 

2902 if self.secondaryjoin is not None: 

2903 self.secondaryjoin = visitors.replacement_traverse( 

2904 self.secondaryjoin, {}, check_fk 

2905 ) 

2906 

2907 def _annotate_present_fks(self): 

2908 if self.secondary is not None: 

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

2910 else: 

2911 secondarycols = set() 

2912 

2913 def is_foreign(a, b): 

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

2915 if a.references(b): 

2916 return a 

2917 elif b.references(a): 

2918 return b 

2919 

2920 if secondarycols: 

2921 if a in secondarycols and b not in secondarycols: 

2922 return a 

2923 elif b in secondarycols and a not in secondarycols: 

2924 return b 

2925 

2926 def visit_binary(binary): 

2927 if not isinstance( 

2928 binary.left, sql.ColumnElement 

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

2930 return 

2931 

2932 if ( 

2933 "foreign" not in binary.left._annotations 

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

2935 ): 

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

2937 if col is not None: 

2938 if col.compare(binary.left): 

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

2940 elif col.compare(binary.right): 

2941 binary.right = binary.right._annotate( 

2942 {"foreign": True} 

2943 ) 

2944 

2945 self.primaryjoin = visitors.cloned_traverse( 

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

2947 ) 

2948 if self.secondaryjoin is not None: 

2949 self.secondaryjoin = visitors.cloned_traverse( 

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

2951 ) 

2952 

2953 def _refers_to_parent_table(self): 

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

2955 comparisons where both columns are in both tables. 

2956 

2957 """ 

2958 pt = self.parent_persist_selectable 

2959 mt = self.child_persist_selectable 

2960 result = [False] 

2961 

2962 def visit_binary(binary): 

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

2964 if ( 

2965 isinstance(c, expression.ColumnClause) 

2966 and isinstance(f, expression.ColumnClause) 

2967 and pt.is_derived_from(c.table) 

2968 and pt.is_derived_from(f.table) 

2969 and mt.is_derived_from(c.table) 

2970 and mt.is_derived_from(f.table) 

2971 ): 

2972 result[0] = True 

2973 

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

2975 return result[0] 

2976 

2977 def _tables_overlap(self): 

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

2979 

2980 return selectables_overlap( 

2981 self.parent_persist_selectable, self.child_persist_selectable 

2982 ) 

2983 

2984 def _annotate_remote(self): 

2985 """Annotate the primaryjoin and secondaryjoin 

2986 structures with 'remote' annotations marking columns 

2987 considered as part of the 'remote' side. 

2988 

2989 """ 

2990 if self._has_remote_annotations: 

2991 return 

2992 

2993 if self.secondary is not None: 

2994 self._annotate_remote_secondary() 

2995 elif self._local_remote_pairs or self._remote_side: 

2996 self._annotate_remote_from_args() 

2997 elif self._refers_to_parent_table(): 

2998 self._annotate_selfref( 

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

3000 ) 

3001 elif self._tables_overlap(): 

3002 self._annotate_remote_with_overlap() 

3003 else: 

3004 self._annotate_remote_distinct_selectables() 

3005 

3006 def _annotate_remote_secondary(self): 

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

3008 when 'secondary' is present. 

3009 

3010 """ 

3011 

3012 def repl(element): 

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

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

3015 

3016 self.primaryjoin = visitors.replacement_traverse( 

3017 self.primaryjoin, {}, repl 

3018 ) 

3019 self.secondaryjoin = visitors.replacement_traverse( 

3020 self.secondaryjoin, {}, repl 

3021 ) 

3022 

3023 def _annotate_selfref(self, fn, remote_side_given): 

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

3025 when the relationship is detected as self-referential. 

3026 

3027 """ 

3028 

3029 def visit_binary(binary): 

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

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

3032 binary.right, expression.ColumnClause 

3033 ): 

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

3035 if fn(binary.left): 

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

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

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

3039 elif not remote_side_given: 

3040 self._warn_non_column_elements() 

3041 

3042 self.primaryjoin = visitors.cloned_traverse( 

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

3044 ) 

3045 

3046 def _annotate_remote_from_args(self): 

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

3048 when the 'remote_side' or '_local_remote_pairs' 

3049 arguments are used. 

3050 

3051 """ 

3052 if self._local_remote_pairs: 

3053 if self._remote_side: 

3054 raise sa_exc.ArgumentError( 

3055 "remote_side argument is redundant " 

3056 "against more detailed _local_remote_side " 

3057 "argument." 

3058 ) 

3059 

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

3061 else: 

3062 remote_side = self._remote_side 

3063 

3064 if self._refers_to_parent_table(): 

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

3066 else: 

3067 

3068 def repl(element): 

3069 # use set() to avoid generating ``__eq__()`` expressions 

3070 # against each element 

3071 if element in set(remote_side): 

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

3073 

3074 self.primaryjoin = visitors.replacement_traverse( 

3075 self.primaryjoin, {}, repl 

3076 ) 

3077 

3078 def _annotate_remote_with_overlap(self): 

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

3080 when the parent/child tables have some set of 

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

3082 relationship. 

3083 

3084 """ 

3085 

3086 def visit_binary(binary): 

3087 binary.left, binary.right = proc_left_right( 

3088 binary.left, binary.right 

3089 ) 

3090 binary.right, binary.left = proc_left_right( 

3091 binary.right, binary.left 

3092 ) 

3093 

3094 check_entities = ( 

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

3096 ) 

3097 

3098 def proc_left_right(left, right): 

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

3100 right, expression.ColumnClause 

3101 ): 

3102 if self.child_persist_selectable.c.contains_column( 

3103 right 

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

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

3106 elif ( 

3107 check_entities 

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

3109 ): 

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

3111 elif ( 

3112 check_entities 

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

3114 ): 

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

3116 else: 

3117 self._warn_non_column_elements() 

3118 

3119 return left, right 

3120 

3121 self.primaryjoin = visitors.cloned_traverse( 

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

3123 ) 

3124 

3125 def _annotate_remote_distinct_selectables(self): 

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

3127 when the parent/child tables are entirely 

3128 separate. 

3129 

3130 """ 

3131 

3132 def repl(element): 

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

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

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

3136 ): 

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

3138 

3139 self.primaryjoin = visitors.replacement_traverse( 

3140 self.primaryjoin, {}, repl 

3141 ) 

3142 

3143 def _warn_non_column_elements(self): 

3144 util.warn( 

3145 "Non-simple column elements in primary " 

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

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

3148 ) 

3149 

3150 def _annotate_local(self): 

3151 """Annotate the primaryjoin and secondaryjoin 

3152 structures with 'local' annotations. 

3153 

3154 This annotates all column elements found 

3155 simultaneously in the parent table 

3156 and the join condition that don't have a 

3157 'remote' annotation set up from 

3158 _annotate_remote() or user-defined. 

3159 

3160 """ 

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

3162 return 

3163 

3164 if self._local_remote_pairs: 

3165 local_side = util.column_set( 

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

3167 ) 

3168 else: 

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

3170 

3171 def locals_(elem): 

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

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

3174 

3175 self.primaryjoin = visitors.replacement_traverse( 

3176 self.primaryjoin, {}, locals_ 

3177 ) 

3178 

3179 def _annotate_parentmapper(self): 

3180 if self.prop is None: 

3181 return 

3182 

3183 def parentmappers_(elem): 

3184 if "remote" in elem._annotations: 

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

3186 elif "local" in elem._annotations: 

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

3188 

3189 self.primaryjoin = visitors.replacement_traverse( 

3190 self.primaryjoin, {}, parentmappers_ 

3191 ) 

3192 

3193 def _check_remote_side(self): 

3194 if not self.local_remote_pairs: 

3195 raise sa_exc.ArgumentError( 

3196 "Relationship %s could " 

3197 "not determine any unambiguous local/remote column " 

3198 "pairs based on join condition and remote_side " 

3199 "arguments. " 

3200 "Consider using the remote() annotation to " 

3201 "accurately mark those elements of the join " 

3202 "condition that are on the remote side of " 

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

3204 ) 

3205 else: 

3206 

3207 not_target = util.column_set( 

3208 self.parent_persist_selectable.c 

3209 ).difference(self.child_persist_selectable.c) 

3210 

3211 for _, rmt in self.local_remote_pairs: 

3212 if rmt in not_target: 

3213 util.warn( 

3214 "Expression %s is marked as 'remote', but these " 

3215 "column(s) are local to the local side. The " 

3216 "remote() annotation is needed only for a " 

3217 "self-referential relationship where both sides " 

3218 "of the relationship refer to the same tables." 

3219 % (rmt,) 

3220 ) 

3221 

3222 def _check_foreign_cols(self, join_condition, primary): 

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

3224 messages.""" 

3225 

3226 can_sync = False 

3227 

3228 foreign_cols = self._gather_columns_with_annotation( 

3229 join_condition, "foreign" 

3230 ) 

3231 

3232 has_foreign = bool(foreign_cols) 

3233 

3234 if primary: 

3235 can_sync = bool(self.synchronize_pairs) 

3236 else: 

3237 can_sync = bool(self.secondary_synchronize_pairs) 

3238 

3239 if ( 

3240 self.support_sync 

3241 and can_sync 

3242 or (not self.support_sync and has_foreign) 

3243 ): 

3244 return 

3245 

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

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

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

3249 if self.support_sync and has_foreign and not can_sync: 

3250 err = ( 

3251 "Could not locate any simple equality expressions " 

3252 "involving locally mapped foreign key columns for " 

3253 "%s join condition " 

3254 "'%s' on relationship %s." 

3255 % ( 

3256 primary and "primary" or "secondary", 

3257 join_condition, 

3258 self.prop, 

3259 ) 

3260 ) 

3261 err += ( 

3262 " Ensure that referencing columns are associated " 

3263 "with a ForeignKey or ForeignKeyConstraint, or are " 

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

3265 "annotation. To allow comparison operators other than " 

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

3267 ) 

3268 

3269 raise sa_exc.ArgumentError(err) 

3270 else: 

3271 err = ( 

3272 "Could not locate any relevant foreign key columns " 

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

3274 % ( 

3275 primary and "primary" or "secondary", 

3276 join_condition, 

3277 self.prop, 

3278 ) 

3279 ) 

3280 err += ( 

3281 " Ensure that referencing columns are associated " 

3282 "with a ForeignKey or ForeignKeyConstraint, or are " 

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

3284 "annotation." 

3285 ) 

3286 raise sa_exc.ArgumentError(err) 

3287 

3288 def _determine_direction(self): 

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

3290 many to many. 

3291 

3292 """ 

3293 if self.secondaryjoin is not None: 

3294 self.direction = MANYTOMANY 

3295 else: 

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

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

3298 

3299 # fk collection which suggests ONETOMANY. 

3300 onetomany_fk = targetcols.intersection(self.foreign_key_columns) 

3301 

3302 # fk collection which suggests MANYTOONE. 

3303 

3304 manytoone_fk = parentcols.intersection(self.foreign_key_columns) 

3305 

3306 if onetomany_fk and manytoone_fk: 

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

3308 # with foreign key. 

3309 # we will gather columns directly from their annotations 

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

3311 # that refers to itself. 

3312 

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

3314 # onetomany. 

3315 onetomany_local = self._gather_columns_with_annotation( 

3316 self.primaryjoin, "remote", "foreign" 

3317 ) 

3318 

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

3320 # suggest manytoone. 

3321 manytoone_local = set( 

3322 [ 

3323 c 

3324 for c in self._gather_columns_with_annotation( 

3325 self.primaryjoin, "foreign" 

3326 ) 

3327 if "remote" not in c._annotations 

3328 ] 

3329 ) 

3330 

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

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

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

3334 if onetomany_local and manytoone_local: 

3335 self_equated = self.remote_columns.intersection( 

3336 self.local_columns 

3337 ) 

3338 onetomany_local = onetomany_local.difference(self_equated) 

3339 manytoone_local = manytoone_local.difference(self_equated) 

3340 

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

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

3343 # ambiguous. 

3344 

3345 if onetomany_local and not manytoone_local: 

3346 self.direction = ONETOMANY 

3347 elif manytoone_local and not onetomany_local: 

3348 self.direction = MANYTOONE 

3349 else: 

3350 raise sa_exc.ArgumentError( 

3351 "Can't determine relationship" 

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

3353 "key columns within the join condition are present " 

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

3355 "Ensure that only those columns referring " 

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

3357 "either via the foreign() annotation or " 

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

3359 ) 

3360 elif onetomany_fk: 

3361 self.direction = ONETOMANY 

3362 elif manytoone_fk: 

3363 self.direction = MANYTOONE 

3364 else: 

3365 raise sa_exc.ArgumentError( 

3366 "Can't determine relationship " 

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

3368 "key columns are present in neither the parent " 

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

3370 ) 

3371 

3372 def _deannotate_pairs(self, collection): 

3373 """provide deannotation for the various lists of 

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

3375 high-overhead __eq__() comparisons against 

3376 original columns mapped. 

3377 

3378 """ 

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

3380 

3381 def _setup_pairs(self): 

3382 sync_pairs = [] 

3383 lrp = util.OrderedSet([]) 

3384 secondary_sync_pairs = [] 

3385 

3386 def go(joincond, collection): 

3387 def visit_binary(binary, left, right): 

3388 if ( 

3389 "remote" in right._annotations 

3390 and "remote" not in left._annotations 

3391 and self.can_be_synced_fn(left) 

3392 ): 

3393 lrp.add((left, right)) 

3394 elif ( 

3395 "remote" in left._annotations 

3396 and "remote" not in right._annotations 

3397 and self.can_be_synced_fn(right) 

3398 ): 

3399 lrp.add((right, left)) 

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

3401 left, right 

3402 ): 

3403 if "foreign" in right._annotations: 

3404 collection.append((left, right)) 

3405 elif "foreign" in left._annotations: 

3406 collection.append((right, left)) 

3407 

3408 visit_binary_product(visit_binary, joincond) 

3409 

3410 for joincond, collection in [ 

3411 (self.primaryjoin, sync_pairs), 

3412 (self.secondaryjoin, secondary_sync_pairs), 

3413 ]: 

3414 if joincond is None: 

3415 continue 

3416 go(joincond, collection) 

3417 

3418 self.local_remote_pairs = self._deannotate_pairs(lrp) 

3419 self.synchronize_pairs = self._deannotate_pairs(sync_pairs) 

3420 self.secondary_synchronize_pairs = self._deannotate_pairs( 

3421 secondary_sync_pairs 

3422 ) 

3423 

3424 _track_overlapping_sync_targets = weakref.WeakKeyDictionary() 

3425 

3426 def _warn_for_conflicting_sync_targets(self): 

3427 if not self.support_sync: 

3428 return 

3429 

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

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

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

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

3434 # impact of this check 

3435 for from_, to_ in [ 

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

3437 ] + [ 

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

3439 ]: 

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

3441 # considering columns that are subject to a overlapping 

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

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

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

3445 # level configuration that benefits from this warning. 

3446 

3447 if to_ not in self._track_overlapping_sync_targets: 

3448 self._track_overlapping_sync_targets[ 

3449 to_ 

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

3451 else: 

3452 other_props = [] 

3453 prop_to_from = self._track_overlapping_sync_targets[to_] 

3454 

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

3456 if ( 

3457 not pr.mapper._dispose_called 

3458 and pr not in self.prop._reverse_property 

3459 and pr.key not in self.prop._overlaps 

3460 and self.prop.key not in pr._overlaps 

3461 # note: the "__*" symbol is used internally by 

3462 # SQLAlchemy as a general means of suppressing the 

3463 # overlaps warning for some extension cases, however 

3464 # this is not currently 

3465 # a publicly supported symbol and may change at 

3466 # any time. 

3467 and "__*" not in self.prop._overlaps 

3468 and "__*" not in pr._overlaps 

3469 and not self.prop.parent.is_sibling(pr.parent) 

3470 and not self.prop.mapper.is_sibling(pr.mapper) 

3471 and not self.prop.parent.is_sibling(pr.mapper) 

3472 and not self.prop.mapper.is_sibling(pr.parent) 

3473 and ( 

3474 self.prop.key != pr.key 

3475 or not self.prop.parent.common_parent(pr.parent) 

3476 ) 

3477 ): 

3478 

3479 other_props.append((pr, fr_)) 

3480 

3481 if other_props: 

3482 util.warn( 

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

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

3485 "If this is not the intention, consider if these " 

3486 "relationships should be linked with " 

3487 "back_populates, or if viewonly=True should be " 

3488 "applied to one or more if they are read-only. " 

3489 "For the less common case that foreign key " 

3490 "constraints are partially overlapping, the " 

3491 "orm.foreign() " 

3492 "annotation can be used to isolate the columns that " 

3493 "should be written towards. To silence this " 

3494 "warning, add the parameter 'overlaps=\"%s\"' to the " 

3495 "'%s' relationship." 

3496 % ( 

3497 self.prop, 

3498 from_, 

3499 to_, 

3500 ", ".join( 

3501 sorted( 

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

3503 for (pr, fr_) in other_props 

3504 ) 

3505 ), 

3506 ",".join(sorted(pr.key for pr, fr in other_props)), 

3507 self.prop, 

3508 ), 

3509 code="qzyx", 

3510 ) 

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

3512 

3513 @util.memoized_property 

3514 def remote_columns(self): 

3515 return self._gather_join_annotations("remote") 

3516 

3517 @util.memoized_property 

3518 def local_columns(self): 

3519 return self._gather_join_annotations("local") 

3520 

3521 @util.memoized_property 

3522 def foreign_key_columns(self): 

3523 return self._gather_join_annotations("foreign") 

3524 

3525 def _gather_join_annotations(self, annotation): 

3526 s = set( 

3527 self._gather_columns_with_annotation(self.primaryjoin, annotation) 

3528 ) 

3529 if self.secondaryjoin is not None: 

3530 s.update( 

3531 self._gather_columns_with_annotation( 

3532 self.secondaryjoin, annotation 

3533 ) 

3534 ) 

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

3536 

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

3538 annotation = set(annotation) 

3539 return set( 

3540 [ 

3541 col 

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

3543 if annotation.issubset(col._annotations) 

3544 ] 

3545 ) 

3546 

3547 def join_targets( 

3548 self, 

3549 source_selectable, 

3550 dest_selectable, 

3551 aliased, 

3552 single_crit=None, 

3553 extra_criteria=(), 

3554 ): 

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

3556 join between them. 

3557 

3558 This takes into account aliasing the join clause 

3559 to reference the appropriate corresponding columns 

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

3561 criterion, equivalent column sets, etc. 

3562 

3563 """ 

3564 # place a barrier on the destination such that 

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

3566 # its internal structure remains fixed 

3567 # regardless of context. 

3568 dest_selectable = _shallow_annotate( 

3569 dest_selectable, {"no_replacement_traverse": True} 

3570 ) 

3571 

3572 primaryjoin, secondaryjoin, secondary = ( 

3573 self.primaryjoin, 

3574 self.secondaryjoin, 

3575 self.secondary, 

3576 ) 

3577 

3578 # adjust the join condition for single table inheritance, 

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

3580 # this is analogous to the 

3581 # "_adjust_for_single_table_inheritance()" method in Query. 

3582 

3583 if single_crit is not None: 

3584 if secondaryjoin is not None: 

3585 secondaryjoin = secondaryjoin & single_crit 

3586 else: 

3587 primaryjoin = primaryjoin & single_crit 

3588 

3589 if extra_criteria: 

3590 if secondaryjoin is not None: 

3591 secondaryjoin = secondaryjoin & sql.and_(*extra_criteria) 

3592 else: 

3593 primaryjoin = primaryjoin & sql.and_(*extra_criteria) 

3594 

3595 if aliased: 

3596 if secondary is not None: 

3597 secondary = secondary._anonymous_fromclause(flat=True) 

3598 primary_aliasizer = ClauseAdapter( 

3599 secondary, exclude_fn=_ColInAnnotations("local") 

3600 ) 

3601 secondary_aliasizer = ClauseAdapter( 

3602 dest_selectable, equivalents=self.child_equivalents 

3603 ).chain(primary_aliasizer) 

3604 if source_selectable is not None: 

3605 primary_aliasizer = ClauseAdapter( 

3606 secondary, exclude_fn=_ColInAnnotations("local") 

3607 ).chain( 

3608 ClauseAdapter( 

3609 source_selectable, 

3610 equivalents=self.parent_equivalents, 

3611 ) 

3612 ) 

3613 

3614 secondaryjoin = secondary_aliasizer.traverse(secondaryjoin) 

3615 else: 

3616 primary_aliasizer = ClauseAdapter( 

3617 dest_selectable, 

3618 exclude_fn=_ColInAnnotations("local"), 

3619 equivalents=self.child_equivalents, 

3620 ) 

3621 if source_selectable is not None: 

3622 primary_aliasizer.chain( 

3623 ClauseAdapter( 

3624 source_selectable, 

3625 exclude_fn=_ColInAnnotations("remote"), 

3626 equivalents=self.parent_equivalents, 

3627 ) 

3628 ) 

3629 secondary_aliasizer = None 

3630 

3631 primaryjoin = primary_aliasizer.traverse(primaryjoin) 

3632 target_adapter = secondary_aliasizer or primary_aliasizer 

3633 target_adapter.exclude_fn = None 

3634 else: 

3635 target_adapter = None 

3636 return ( 

3637 primaryjoin, 

3638 secondaryjoin, 

3639 secondary, 

3640 target_adapter, 

3641 dest_selectable, 

3642 ) 

3643 

3644 def create_lazy_clause(self, reverse_direction=False): 

3645 binds = util.column_dict() 

3646 equated_columns = util.column_dict() 

3647 

3648 has_secondary = self.secondaryjoin is not None 

3649 

3650 if has_secondary: 

3651 lookup = collections.defaultdict(list) 

3652 for l, r in self.local_remote_pairs: 

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

3654 equated_columns[r] = l 

3655 elif not reverse_direction: 

3656 for l, r in self.local_remote_pairs: 

3657 equated_columns[r] = l 

3658 else: 

3659 for l, r in self.local_remote_pairs: 

3660 equated_columns[l] = r 

3661 

3662 def col_to_bind(col): 

3663 

3664 if ( 

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

3666 or reverse_direction 

3667 and ( 

3668 (has_secondary and col in lookup) 

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

3670 ) 

3671 ): 

3672 if col not in binds: 

3673 binds[col] = sql.bindparam( 

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

3675 ) 

3676 return binds[col] 

3677 return None 

3678 

3679 lazywhere = self.primaryjoin 

3680 if self.secondaryjoin is None or not reverse_direction: 

3681 lazywhere = visitors.replacement_traverse( 

3682 lazywhere, {}, col_to_bind 

3683 ) 

3684 

3685 if self.secondaryjoin is not None: 

3686 secondaryjoin = self.secondaryjoin 

3687 if reverse_direction: 

3688 secondaryjoin = visitors.replacement_traverse( 

3689 secondaryjoin, {}, col_to_bind 

3690 ) 

3691 lazywhere = sql.and_(lazywhere, secondaryjoin) 

3692 

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

3694 

3695 return lazywhere, bind_to_col, equated_columns 

3696 

3697 

3698class _ColInAnnotations(object): 

3699 """Serializable object that tests for a name in c._annotations.""" 

3700 

3701 __slots__ = ("name",) 

3702 

3703 def __init__(self, name): 

3704 self.name = name 

3705 

3706 def __call__(self, c): 

3707 return self.name in c._annotations