From: Mike Bayer Date: Fri, 11 Apr 2008 15:58:18 +0000 (+0000) Subject: - re-established viewonly relation() configurations that X-Git-Tag: rel_0_5beta1~188 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ce109f7adf081c3b42802d0de1a2c92bc56f9809;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - re-established viewonly relation() configurations that join across multiple tables. --- diff --git a/CHANGES b/CHANGES index 9abc4a249a..a045b0bf5a 100644 --- a/CHANGES +++ b/CHANGES @@ -6,9 +6,12 @@ CHANGES ===== - orm - Fix to the recent relation() refactoring which fixes - exotic relations which join between local and remote table + exotic viewonly relations which join between local and remote table multiple times, with a common column shared between the joins. + + - Also re-established viewonly relation() configurations that + join across multiple tables. - removed ancient assertion that mapped selectables require "alias names" - the mapper creates its own alias now if diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index c7a10ccf35..c4bb323de5 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -544,12 +544,12 @@ class PropertyLoader(StrategizedProperty): arg_foreign_keys = self.foreign_keys eq_pairs = criterion_as_pairs(self.primaryjoin, consider_as_foreign_keys=arg_foreign_keys, any_operator=self.viewonly) - eq_pairs = [(l, r) for l, r in eq_pairs if self.__col_is_part_of_mappings(l) and self.__col_is_part_of_mappings(r)] + eq_pairs = [(l, r) for l, r in eq_pairs if (self.__col_is_part_of_mappings(l) and self.__col_is_part_of_mappings(r)) or r in arg_foreign_keys] if not eq_pairs: if not self.viewonly and criterion_as_pairs(self.primaryjoin, consider_as_foreign_keys=arg_foreign_keys, any_operator=True): - raise exceptions.ArgumentError("Could not locate any equated column pairs for primaryjoin condition '%s' on relation %s. " - "If no equated pairs exist, the relation must be marked as viewonly=True." % (self.primaryjoin, self) + raise exceptions.ArgumentError("Could not locate any equated, locally mapped column pairs for primaryjoin condition '%s' on relation %s. " + "For more relaxed rules on join conditions, the relation may be marked as viewonly=True." % (self.primaryjoin, self) ) else: raise exceptions.ArgumentError("Could not determine relation direction for primaryjoin condition '%s', on relation %s. " @@ -561,12 +561,12 @@ class PropertyLoader(StrategizedProperty): if self.secondaryjoin: sq_pairs = criterion_as_pairs(self.secondaryjoin, consider_as_foreign_keys=arg_foreign_keys, any_operator=self.viewonly) - sq_pairs = [(l, r) for l, r in sq_pairs if self.__col_is_part_of_mappings(l) and self.__col_is_part_of_mappings(r)] + sq_pairs = [(l, r) for l, r in sq_pairs if (self.__col_is_part_of_mappings(l) and self.__col_is_part_of_mappings(r)) or r in arg_foreign_keys] if not sq_pairs: if not self.viewonly and criterion_as_pairs(self.secondaryjoin, consider_as_foreign_keys=arg_foreign_keys, any_operator=True): - raise exceptions.ArgumentError("Could not locate any equated column pairs for secondaryjoin condition '%s' on relation %s. " - "If no equated pairs exist, the relation must be marked as viewonly=True." % (self.secondaryjoin, self) + raise exceptions.ArgumentError("Could not locate any equated, locally mapped column pairs for secondaryjoin condition '%s' on relation %s. " + "For more relaxed rules on join conditions, the relation may be marked as viewonly=True." % (self.secondaryjoin, self) ) else: raise exceptions.ArgumentError("Could not determine relation direction for secondaryjoin condition '%s', on relation %s. " @@ -599,6 +599,15 @@ class PropertyLoader(StrategizedProperty): self.remote_side, self.local_side = [util.OrderedSet(s) for s in zip(*eq_pairs)] else: self.local_side, self.remote_side = [util.OrderedSet(s) for s in zip(*eq_pairs)] + + if self.direction is ONETOMANY: + for l in self.local_side: + if not self.__col_is_part_of_mappings(l): + raise exceptions.ArgumentError("Local column '%s' is not part of mapping %s. Specify remote_side argument to indicate which column lazy join condition should compare against." % (l, self.parent)) + elif self.direction is MANYTOONE: + for r in self.remote_side: + if not self.__col_is_part_of_mappings(r): + raise exceptions.ArgumentError("Remote column '%s' is not part of mapping %s. Specify remote_side argument to indicate which column lazy join condition should bind." % (r, self.mapper)) def __determine_direction(self): """Determine our *direction*, i.e. do we represent one to diff --git a/test/orm/relationships.py b/test/orm/relationships.py index 6cc24f411a..22b69af2aa 100644 --- a/test/orm/relationships.py +++ b/test/orm/relationships.py @@ -977,6 +977,81 @@ class ViewOnlyTest5(ORMTest): sess.clear() self.assertEquals(sess.query(Foo).filter_by(id=f1.id).one(), Foo(bars=[Bar(data='b1'), Bar(data='b2'), Bar(data='b4')])) self.assertEquals(sess.query(Foo).filter_by(id=f2.id).one(), Foo(bars=[Bar(data='b3'), Bar(data='b4')])) + +class ViewOnlyTest6(ORMTest): + """test a long primaryjoin condition""" + def define_tables(self, metadata): + global t1, t2, t3, t2tot3 + t1 = Table('t1', metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)) + ) + t2 = Table('t2', metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)), + Column('t1id', Integer, ForeignKey('t1.id')), + ) + t3 = Table('t3', metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)) + ) + t2tot3 = Table('t2tot3', metadata, + Column('t2id', Integer, ForeignKey('t2.id')), + Column('t3id', Integer, ForeignKey('t3.id')), + ) + + def test_basic(self): + class T1(fixtures.Base): + pass + class T2(fixtures.Base): + pass + class T3(fixtures.Base): + pass + + mapper(T1, t1, properties={ + 't3s':relation(T3, primaryjoin=and_( + t1.c.id==t2.c.t1id, + t2.c.id==t2tot3.c.t2id, + t3.c.id==t2tot3.c.t3id + ), + viewonly=True, + foreign_keys=t3.c.id, remote_side=t2.c.t1id) + }) + mapper(T2, t2, properties={ + 't1':relation(T1), + 't3s':relation(T3, secondary=t2tot3) + }) + mapper(T3, t3) + + sess = create_session() + sess.save(T2(data='t2', t1=T1(data='t1'), t3s=[T3(data='t3')])) + sess.flush() + sess.clear() + + a = sess.query(T1).first() + self.assertEquals(a.t3s, [T3(data='t3')]) + + def test_remote_side_escalation(self): + class T1(fixtures.Base): + pass + class T2(fixtures.Base): + pass + class T3(fixtures.Base): + pass + + mapper(T1, t1, properties={ + 't3s':relation(T3, primaryjoin=and_( + t1.c.id==t2.c.t1id, + t2.c.id==t2tot3.c.t2id, + t3.c.id==t2tot3.c.t3id + ),viewonly=True, foreign_keys=t3.c.id) + }) + mapper(T2, t2, properties={ + 't1':relation(T1), + 't3s':relation(T3, secondary=t2tot3) + }) + mapper(T3, t3) + self.assertRaisesMessage(exceptions.ArgumentError, "Specify remote_side argument", compile_mappers) class InvalidRelationEscalationTest(ORMTest): def define_tables(self, metadata): @@ -1018,7 +1093,7 @@ class InvalidRelationEscalationTest(ORMTest): }) mapper(Bar, bars) - self.assertRaisesMessage(exceptions.ArgumentError, "Could not locate any equated column pairs for primaryjoin condition", compile_mappers) + self.assertRaisesMessage(exceptions.ArgumentError, "Could not locate any equated, locally mapped column pairs for primaryjoin condition", compile_mappers) def test_no_equated_self_ref(self): mapper(Foo, foos, properties={ @@ -1034,7 +1109,7 @@ class InvalidRelationEscalationTest(ORMTest): }) mapper(Bar, bars) - self.assertRaisesMessage(exceptions.ArgumentError, "Could not locate any equated column pairs for primaryjoin condition", compile_mappers) + self.assertRaisesMessage(exceptions.ArgumentError, "Could not locate any equated, locally mapped column pairs for primaryjoin condition", compile_mappers) def test_no_equated_viewonly(self): mapper(Foo, foos, properties={ @@ -1130,7 +1205,7 @@ class InvalidRelationEscalationTestM2M(ORMTest): }) mapper(Bar, bars) - self.assertRaisesMessage(exceptions.ArgumentError, "Could not locate any equated column pairs for secondaryjoin condition", compile_mappers) + self.assertRaisesMessage(exceptions.ArgumentError, "Could not locate any equated, locally mapped column pairs for secondaryjoin condition", compile_mappers) if __name__ == "__main__":