diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-04-12 13:45:08 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-04-12 13:45:08 -0400 |
commit | 35a488ae5f4127b4bc2b4e5bfc388749e222a4e4 (patch) | |
tree | 7f9dfd6a1ea3d764e929ea6acb046c13dca02539 | |
parent | fc702c9d809878c5a5d85876bfa23e17177940c2 (diff) | |
download | sqlalchemy-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.rst | 12 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/relationships.py | 9 | ||||
-rw-r--r-- | test/orm/test_rel_fn.py | 43 |
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( |