~ubuntu-branches/debian/sid/sqlalchemy/sid

« back to all changes in this revision

Viewing changes to lib/sqlalchemy/orm/relationships.py

  • Committer: Package Import Robot
  • Author(s): Piotr Ożarowski
  • Date: 2014-06-27 20:17:13 UTC
  • mfrom: (1.4.28)
  • Revision ID: package-import@ubuntu.com-20140627201713-g6p1kq8q1qenztrv
Tags: 0.9.6-1
* New upstream release
* Remove Python 3.X build tag files, thanks to Matthias Urlichs for the
  patch (closes: #747852)
* python-fdb isn't in the Debian archive yet so default dialect for firebird://
  URLs is changed to obsolete kinterbasdb, thanks to Russell Stuart for the
  patch (closes: #752145)

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
and `secondaryjoin` aspects of :func:`.relationship`.
13
13
 
14
14
"""
15
 
 
 
15
from __future__ import absolute_import
16
16
from .. import sql, util, exc as sa_exc, schema, log
17
17
 
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
 
30
import collections
30
31
 
31
32
def remote(expr):
32
33
    """Annotate a portion of a primaryjoin expression
665
666
 
666
667
          .. seealso::
667
668
 
668
 
            :ref:`self_referential` - in-depth explaination of how
 
669
            :ref:`self_referential` - in-depth explanation of how
669
670
            :paramref:`~.relationship.remote_side`
670
671
            is used to configure self-referential relationships.
671
672
 
2391
2392
 
2392
2393
            if onetomany_fk and manytoone_fk:
2393
2394
                # fks on both sides.  test for overlap of local/remote
2394
 
                # with foreign key
2395
 
                self_equated = self.remote_columns.intersection(
2396
 
                                        self.local_columns
2397
 
                                    )
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)
 
2395
                # with foreign key.
 
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.
 
2399
 
 
2400
                # 1. columns that are both remote and FK suggest
 
2401
                # onetomany.
 
2402
                onetomany_local = self._gather_columns_with_annotation(
 
2403
                                        self.primaryjoin, "remote", "foreign")
 
2404
 
 
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(
 
2409
                                                    self.primaryjoin,
 
2410
                                                    "foreign")
 
2411
                                        if "remote" not in c._annotations])
 
2412
 
 
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(
 
2418
                                            self.local_columns
 
2419
                                        )
 
2420
                    onetomany_local = onetomany_local.difference(self_equated)
 
2421
                    manytoone_local = manytoone_local.difference(self_equated)
 
2422
 
 
2423
                # at this point, if only one or the other collection is
 
2424
                # present, we know the direction, otherwise it's still
 
2425
                # ambiguous.
 
2426
 
2404
2427
                if onetomany_local and not manytoone_local:
2405
2428
                    self.direction = ONETOMANY
2406
2429
                elif manytoone_local and not onetomany_local:
2585
2608
 
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()
2591
2613
 
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
2597
2618
        else:
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
2605
2624
 
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:
2611
2631
                            return None
2612
 
                else:
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]
2618
2636
            return None
2619
2637
 
2620
 
        lazywhere = self.deannotated_primaryjoin
2621
 
 
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)
2625
2642
 
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)
2632
2649
 
2633
2650
        bind_to_col = dict((binds[col].key, col) for col in binds)
2634
2651
 
 
2652
        # this is probably not necessary
 
2653
        lazywhere = _deep_deannotate(lazywhere)
 
2654
 
2635
2655
        return lazywhere, bind_to_col, equated_columns
2636
2656
 
2637
2657
class _ColInAnnotations(object):