]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Made a small improvement to the heuristics of relationship when
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 12 Apr 2015 17:45:08 +0000 (13:45 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 12 Apr 2015 17:45:08 +0000 (13:45 -0400)
determining remote side with semi-self-referential (e.g. two joined
inh subclasses referring to each other), non-simple join conditions
such that the parententity is taken into account and can reduce the
need for using the ``remote()`` annotation; this can restore some
cases that might have worked without the annotation prior to 0.9.4
via :ticket:`2948`. fixes #3364

doc/build/changelog/changelog_10.rst
lib/sqlalchemy/orm/relationships.py
test/orm/test_rel_fn.py

index 404e6f1c8920ef7cce8007eaf61863a2e1702182..17d8942d6947ca46aa9ef58e38b7d56fec929c4e 100644 (file)
 .. changelog::
     :version: 1.0.0
 
+    .. change::
+        :tags: bug, orm
+        :tickets: 3364
+
+        Made a small improvement to the heuristics of relationship when
+        determining remote side with semi-self-referential (e.g. two joined
+        inh subclasses referring to each other), non-simple join conditions
+        such that the parententity is taken into account and can reduce the
+        need for using the ``remote()`` annotation; this can restore some
+        cases that might have worked without the annotation prior to 0.9.4
+        via :ticket:`2948`.
+
     .. change::
         :tags: bug, mssql
         :tickets: 3360
index e36a644da9b8fbd4f8c377c0738c611d5b290605..b649c9e217c7e4e7ad0b98ef9ec7d742854d0fd5 100644 (file)
@@ -2329,12 +2329,21 @@ class JoinCondition(object):
             binary.right, binary.left = proc_left_right(binary.right,
                                                         binary.left)
 
+        check_entities = self.prop is not None and \
+            self.prop.mapper is not self.prop.parent
+
         def proc_left_right(left, right):
             if isinstance(left, expression.ColumnClause) and \
                     isinstance(right, expression.ColumnClause):
                 if self.child_selectable.c.contains_column(right) and \
                         self.parent_selectable.c.contains_column(left):
                     right = right._annotate({"remote": True})
+            elif check_entities and \
+                    right._annotations.get('parentmapper') is self.prop.mapper:
+                right = right._annotate({"remote": True})
+            elif check_entities and \
+                    left._annotations.get('parentmapper') is self.prop.mapper:
+                left = left._annotate({"remote": True})
             else:
                 self._warn_non_column_elements()
 
index 230f3b18afde2a88c83fdd0f0151e50d96273077..37c503a127cbc5dee926c0a079a21367032c4ec4 100644 (file)
@@ -3,9 +3,9 @@ from sqlalchemy.testing import assert_raises_message, eq_, \
 from sqlalchemy.testing import fixtures
 from sqlalchemy.orm import relationships, foreign, remote
 from sqlalchemy import MetaData, Table, Column, ForeignKey, Integer, \
-    select, ForeignKeyConstraint, exc, func, and_, String
+    select, ForeignKeyConstraint, exc, func, and_, String, Boolean
 from sqlalchemy.orm.interfaces import ONETOMANY, MANYTOONE, MANYTOMANY
-
+from sqlalchemy.testing import mock
 
 class _JoinFixtures(object):
     @classmethod
@@ -71,6 +71,7 @@ class _JoinFixtures(object):
         )
         cls.base = Table('base', m,
             Column('id', Integer, primary_key=True),
+            Column('flag', Boolean)
         )
         cls.sub = Table('sub', m,
             Column('id', Integer, ForeignKey('base.id'),
@@ -504,6 +505,33 @@ class _JoinFixtures(object):
                 foreign(remote(self.selfref.c.sid)))
         )
 
+    def _join_fixture_inh_selfref_w_entity(self, **kw):
+        # MARKMARK
+
+        fake_logger = mock.Mock(info=lambda *arg, **kw: None)
+        prop = mock.Mock(
+            parent=mock.Mock(),
+            mapper=mock.Mock(),
+            logger=fake_logger
+        )
+        local_selectable = self.base.join(self.sub)
+        remote_selectable = self.base.join(self.sub_w_sub_rel)
+
+        sub_w_sub_rel__sub_id = self.sub_w_sub_rel.c.sub_id._annotate(
+            {'parentmapper': prop.mapper})
+        sub__id = self.sub.c.id._annotate({'parentmapper': prop.parent})
+        sub_w_sub_rel__flag = self.base.c.flag._annotate(
+            {"parentmapper": prop.mapper})
+        return relationships.JoinCondition(
+            local_selectable, remote_selectable,
+            local_selectable, remote_selectable,
+            primaryjoin=and_(
+                sub_w_sub_rel__sub_id == sub__id,
+                sub_w_sub_rel__flag == True
+            ),
+            prop=prop
+        )
+
     def _assert_non_simple_warning(self, fn):
         assert_raises_message(
             exc.SAWarning,
@@ -904,6 +932,17 @@ class ColumnCollectionsTest(_JoinFixtures, fixtures.TestBase,
             [(self.purely_single_col.c.path, self.purely_single_col.c.path)]
         )
 
+    def test_determine_local_remote_pairs_inh_selfref_w_entities(self):
+        joincond = self._join_fixture_inh_selfref_w_entity()
+        eq_(
+            joincond.local_remote_pairs,
+            [(self.sub.c.id, self.sub_w_sub_rel.c.sub_id)]
+        )
+        eq_(
+            joincond.remote_columns,
+            set([self.base.c.flag, self.sub_w_sub_rel.c.sub_id])
+        )
+
 class DirectionTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL):
     def test_determine_direction_compound_2(self):
         joincond = self._join_fixture_compound_expression_2(