12
12
and `secondaryjoin` aspects of :func:`.relationship`.
15
from __future__ import absolute_import
16
16
from .. import sql, util, exc as sa_exc, schema, log
18
18
from .util import CascadeOptions, _orm_annotate, _orm_deannotate
27
27
from .interfaces import MANYTOMANY, MANYTOONE, ONETOMANY, StrategizedProperty, PropComparator
28
28
from ..inspection import inspect
29
29
from . import mapper as mapperlib
32
33
"""Annotate a portion of a primaryjoin expression
2392
2393
if onetomany_fk and manytoone_fk:
2393
2394
# fks on both sides. test for overlap of local/remote
2395
self_equated = self.remote_columns.intersection(
2398
onetomany_local = self.remote_columns.\
2399
intersection(self.foreign_key_columns).\
2400
difference(self_equated)
2401
manytoone_local = self.local_columns.\
2402
intersection(self.foreign_key_columns).\
2403
difference(self_equated)
2396
# we will gather columns directly from their annotations
2397
# without deannotating, so that we can distinguish on a column
2398
# that refers to itself.
2400
# 1. columns that are both remote and FK suggest
2402
onetomany_local = self._gather_columns_with_annotation(
2403
self.primaryjoin, "remote", "foreign")
2405
# 2. columns that are FK but are not remote (e.g. local)
2406
# suggest manytoone.
2407
manytoone_local = set([c for c in
2408
self._gather_columns_with_annotation(
2411
if "remote" not in c._annotations])
2413
# 3. if both collections are present, remove columns that
2414
# refer to themselves. This is for the case of
2415
# and_(Me.id == Me.remote_id, Me.version == Me.version)
2416
if onetomany_local and manytoone_local:
2417
self_equated = self.remote_columns.intersection(
2420
onetomany_local = onetomany_local.difference(self_equated)
2421
manytoone_local = manytoone_local.difference(self_equated)
2423
# at this point, if only one or the other collection is
2424
# present, we know the direction, otherwise it's still
2404
2427
if onetomany_local and not manytoone_local:
2405
2428
self.direction = ONETOMANY
2406
2429
elif manytoone_local and not onetomany_local:
2586
2609
def create_lazy_clause(self, reverse_direction=False):
2587
2610
binds = util.column_dict()
2588
lookup = util.column_dict()
2611
lookup = collections.defaultdict(list)
2589
2612
equated_columns = util.column_dict()
2590
being_replaced = set()
2592
2614
if reverse_direction and self.secondaryjoin is None:
2593
2615
for l, r in self.local_remote_pairs:
2594
_list = lookup.setdefault(r, [])
2595
_list.append((r, l))
2616
lookup[r].append((r, l))
2596
2617
equated_columns[l] = r
2598
2619
# replace all "local side" columns, which is
2599
2620
# anything that isn't marked "remote"
2600
being_replaced.update(self.local_columns)
2601
2621
for l, r in self.local_remote_pairs:
2602
_list = lookup.setdefault(l, [])
2603
_list.append((l, r))
2622
lookup[l].append((l, r))
2604
2623
equated_columns[r] = l
2606
2625
def col_to_bind(col):
2607
if col in being_replaced or col in lookup:
2626
if (reverse_direction and col in lookup) or \
2627
(not reverse_direction and "local" in col._annotations):
2608
2628
if col in lookup:
2609
2629
for tobind, equated in lookup[col]:
2610
2630
if equated in binds:
2613
assert not reverse_direction
2614
2632
if col not in binds:
2615
2633
binds[col] = sql.bindparam(
2616
2634
None, None, type_=col.type, unique=True)
2617
2635
return binds[col]
2620
lazywhere = self.deannotated_primaryjoin
2622
if self.deannotated_secondaryjoin is None or not reverse_direction:
2638
lazywhere = self.primaryjoin
2639
if self.secondaryjoin is None or not reverse_direction:
2623
2640
lazywhere = visitors.replacement_traverse(
2624
2641
lazywhere, {}, col_to_bind)
2626
if self.deannotated_secondaryjoin is not None:
2627
secondaryjoin = self.deannotated_secondaryjoin
2643
if self.secondaryjoin is not None:
2644
secondaryjoin = self.secondaryjoin
2628
2645
if reverse_direction:
2629
2646
secondaryjoin = visitors.replacement_traverse(
2630
2647
secondaryjoin, {}, col_to_bind)
2633
2650
bind_to_col = dict((binds[col].key, col) for col in binds)
2652
# this is probably not necessary
2653
lazywhere = _deep_deannotate(lazywhere)
2635
2655
return lazywhere, bind_to_col, equated_columns
2637
2657
class _ColInAnnotations(object):