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. "
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. "
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
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):
})
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={
})
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={
})
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__":