summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-04-12 13:45:08 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2015-04-12 13:45:08 -0400
commit35a488ae5f4127b4bc2b4e5bfc388749e222a4e4 (patch)
tree7f9dfd6a1ea3d764e929ea6acb046c13dca02539
parentfc702c9d809878c5a5d85876bfa23e17177940c2 (diff)
downloadsqlalchemy-35a488ae5f4127b4bc2b4e5bfc388749e222a4e4.tar.gz
- 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`. fixes #3364
-rw-r--r--doc/build/changelog/changelog_10.rst12
-rw-r--r--lib/sqlalchemy/orm/relationships.py9
-rw-r--r--test/orm/test_rel_fn.py43
3 files changed, 62 insertions, 2 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst
index 404e6f1c8..17d8942d6 100644
--- a/doc/build/changelog/changelog_10.rst
+++ b/doc/build/changelog/changelog_10.rst
@@ -19,6 +19,18 @@
: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
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index e36a644da..b649c9e21 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -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()
diff --git a/test/orm/test_rel_fn.py b/test/orm/test_rel_fn.py
index 230f3b18a..37c503a12 100644
--- a/test/orm/test_rel_fn.py
+++ b/test/orm/test_rel_fn.py
@@ -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(