From: Mike Bayer Date: Fri, 28 Sep 2012 15:00:53 +0000 (-0400) Subject: - fix annotation transfer when producing m2m backref, [ticket:2578] X-Git-Tag: rel_0_8_0b1~110^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4aba2eb6017de5d4b56d4aa34af87f2ebab903b0;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - fix annotation transfer when producing m2m backref, [ticket:2578] --- diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index ad48234c26..f8288f5fb3 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -1167,8 +1167,10 @@ class RelationshipProperty(StrategizedProperty): # for many to many, just switch primaryjoin/ # secondaryjoin. use the annotated # pj/sj on the _join_condition. - pj = kwargs.pop('primaryjoin', self._join_condition.secondaryjoin) - sj = kwargs.pop('secondaryjoin', self._join_condition.primaryjoin) + pj = kwargs.pop('primaryjoin', + self._join_condition.secondaryjoin_minus_local) + sj = kwargs.pop('secondaryjoin', + self._join_condition.primaryjoin_minus_local) else: pj = kwargs.pop('primaryjoin', self._join_condition.primaryjoin_reverse_remote) diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index dd6f2442b3..c861edf836 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -170,6 +170,12 @@ class JoinCondition(object): log.info('%s local/remote pairs [%s]', self.prop, ','.join('(%s / %s)' % (l, r) for (l, r) in self.local_remote_pairs)) + log.info('%s remote columns [%s]', self.prop, + ','.join('%s' % col for col in self.remote_columns) + ) + log.info('%s local columns [%s]', self.prop, + ','.join('%s' % col for col in self.local_columns) + ) log.info('%s relationship direction %s', self.prop, self.direction) @@ -266,6 +272,14 @@ class JoinCondition(object): "foreign key reference to the parent table." % self.prop) + @property + def primaryjoin_minus_local(self): + return _deep_deannotate(self.primaryjoin, values=("local", "remote")) + + @property + def secondaryjoin_minus_local(self): + return _deep_deannotate(self.secondaryjoin, values=("local", "remote")) + @util.memoized_property def primaryjoin_reverse_remote(self): """Return the primaryjoin condition suitable for the diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 0b73f9b7af..8186aa507a 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -737,7 +737,7 @@ class SubqueryLoader(AbstractRelationshipLoader): subq_mapper = orm_util._class_to_mapper(subq_path[0]) # determine attributes of the leftmost mapper - if self.parent.isa(subq_mapper) and self.key==subq_path[1]: + if self.parent.isa(subq_mapper) and self.key == subq_path[1]: leftmost_mapper, leftmost_prop = \ self.parent, self.parent_property else: diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index e560b871ab..9761aeae9f 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -862,6 +862,9 @@ class _symbol(int): def __reduce__(self): return symbol, (self.name, "x", int(self)) + def __str__(self): + return repr(self) + def __repr__(self): return "" % self.name diff --git a/test/orm/test_rel_fn.py b/test/orm/test_rel_fn.py index f5fa1d4c97..dfe0db4885 100644 --- a/test/orm/test_rel_fn.py +++ b/test/orm/test_rel_fn.py @@ -135,6 +135,22 @@ class _JoinFixtures(object): **kw ) + def _join_fixture_m2m_backref(self, **kw): + """return JoinCondition in the same way RelationshipProperty + calls it for a backref on an m2m. + + """ + j1 = self._join_fixture_m2m() + return j1, relationships.JoinCondition( + self.m2mright, + self.m2mleft, + self.m2mright, + self.m2mleft, + secondary=self.m2msecondary, + primaryjoin=j1.secondaryjoin_minus_local, + secondaryjoin=j1.primaryjoin_minus_local + ) + def _join_fixture_o2m(self, **kw): return relationships.JoinCondition( self.left, @@ -688,16 +704,42 @@ class ColumnCollectionsTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL ) def test_determine_local_remote_pairs_m2m_backref(self): - joincond = self._join_fixture_m2m() - joincond2 = self._join_fixture_m2m( - primaryjoin=joincond.secondaryjoin, - secondaryjoin=joincond.primaryjoin - ) + j1, j2 = self._join_fixture_m2m_backref() eq_( - joincond.local_remote_pairs, + j1.local_remote_pairs, [(self.m2mleft.c.id, self.m2msecondary.c.lid), (self.m2mright.c.id, self.m2msecondary.c.rid)] ) + eq_( + j2.local_remote_pairs, + [ + (self.m2mright.c.id, self.m2msecondary.c.rid), + (self.m2mleft.c.id, self.m2msecondary.c.lid), + ] + ) + + def test_determine_local_columns_m2m_backref(self): + j1, j2 = self._join_fixture_m2m_backref() + eq_( + j1.local_columns, + set([self.m2mleft.c.id]) + ) + eq_( + j2.local_columns, + set([self.m2mright.c.id]) + ) + + def test_determine_remote_columns_m2m_backref(self): + j1, j2 = self._join_fixture_m2m_backref() + eq_( + j1.remote_columns, + set([self.m2msecondary.c.lid, self.m2msecondary.c.rid]) + ) + eq_( + j2.remote_columns, + set([self.m2msecondary.c.lid, self.m2msecondary.c.rid]) + ) + def test_determine_remote_columns_m2o_selfref(self): joincond = self._join_fixture_m2o_selfref() diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py index 394a1fe7ac..c1cbc0907e 100644 --- a/test/orm/test_relationships.py +++ b/test/orm/test_relationships.py @@ -2706,9 +2706,9 @@ class InvalidRelationshipEscalationTestM2M(_RelationshipErrors, fixtures.MappedT mapper(Foo, foos, properties={ 'bars': relationship(Bar, secondary=foobars_with_many_columns, - primaryjoin=foos.c.id== + primaryjoin=foos.c.id == foobars_with_many_columns.c.fid, - secondaryjoin=foobars_with_many_columns.c.bid== + secondaryjoin=foobars_with_many_columns.c.bid == bars.c.id)}) mapper(Bar, bars) sa.orm.configure_mappers() @@ -2721,6 +2721,31 @@ class InvalidRelationshipEscalationTestM2M(_RelationshipErrors, fixtures.MappedT [(bars.c.id, foobars_with_many_columns.c.bid)] ) + def test_local_col_setup(self): + foobars_with_fks, bars, Bar, Foo, foos = ( + self.tables.foobars_with_fks, + self.tables.bars, + self.classes.Bar, + self.classes.Foo, + self.tables.foos) + + # ensure m2m backref is set up with correct annotations + # [ticket:2578] + mapper(Foo, foos, properties={ + 'bars': relationship(Bar, secondary=foobars_with_fks, backref="foos") + }) + mapper(Bar, bars) + sa.orm.configure_mappers() + eq_( + Foo.bars.property._join_condition.local_columns, + set([foos.c.id]) + ) + eq_( + Bar.foos.property._join_condition.local_columns, + set([bars.c.id]) + ) + + def test_bad_primaryjoin(self): foobars_with_fks, bars, Bar, foobars, Foo, foos = (self.tables.foobars_with_fks,