From: Mike Bayer Date: Sat, 25 Feb 2012 22:10:06 +0000 (-0500) Subject: start to work on error messages, allow foreign_keys as only argument X-Git-Tag: rel_0_8_0b1~477^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d37320306560c3d758ed65563d53aa9500095a49;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git start to work on error messages, allow foreign_keys as only argument if otherwise can't determine join condition due to no fks --- diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py index 91ffc2811b..f28bd8a079 100644 --- a/lib/sqlalchemy/exc.py +++ b/lib/sqlalchemy/exc.py @@ -25,6 +25,13 @@ class ArgumentError(SQLAlchemyError): """ +class NoForeignKeysError(ArgumentError): + """Raised when no foreign keys can be located between two selectables + during a join.""" + +class AmbiguousForeignKeysError(ArgumentError): + """Raised when more than one foreign key matching can be located + between two selectables during a join.""" class CircularDependencyError(SQLAlchemyError): """Raised by topological sorts when a circular dependency is detected. diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index edb7498e03..76e219efee 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -80,11 +80,11 @@ class JoinCondition(object): self._annotate_fks() self._annotate_remote() self._annotate_local() - self._determine_direction() self._setup_pairs() self._check_foreign_cols(self.primaryjoin, True) if self.secondaryjoin is not None: self._check_foreign_cols(self.secondaryjoin, False) + self._determine_direction() self._check_remote_side() self._log_joins() @@ -134,27 +134,68 @@ class JoinCondition(object): join_condition( self.child_selectable, self.secondary, - a_subset=self.child_local_selectable) + a_subset=self.child_local_selectable, + consider_as_foreign_keys=\ + self.consider_as_foreign_keys or None + ) if self.primaryjoin is None: self.primaryjoin = \ join_condition( self.parent_selectable, self.secondary, - a_subset=self.parent_local_selectable) + a_subset=self.parent_local_selectable, + consider_as_foreign_keys=\ + self.consider_as_foreign_keys or None + ) else: if self.primaryjoin is None: self.primaryjoin = \ join_condition( self.parent_selectable, self.child_selectable, - a_subset=self.parent_local_selectable) - except sa_exc.ArgumentError, e: - raise sa_exc.ArgumentError("Could not determine join " - "condition between parent/child tables on " - "relationship %s. Specify a 'primaryjoin' " - "expression. If 'secondary' is present, " - "'secondaryjoin' is needed as well." - % self.prop) + a_subset=self.parent_local_selectable, + consider_as_foreign_keys=\ + self.consider_as_foreign_keys or None + ) + except sa_exc.NoForeignKeysError, nfke: + if self.secondary is not None: + raise sa_exc.NoForeignKeysError("Could not determine join " + "condition between parent/child tables on " + "relationship %s - there are no foreign keys " + "linking these tables via secondary table '%s'. " + "Ensure that referencing columns are associated with a "\ + "ForeignKey or ForeignKeyConstraint, or specify 'primaryjoin' "\ + "and 'secondaryjoin' expressions." + % (self.prop, self.secondary)) + else: + raise sa_exc.NoForeignKeysError("Could not determine join " + "condition between parent/child tables on " + "relationship %s - there are no foreign keys " + "linking these tables. " + "Ensure that referencing columns are associated with a " + "ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' " + "expression." + % self.prop) + except sa_exc.AmbiguousForeignKeysError, afke: + if self.secondary is not None: + raise sa_exc.AmbiguousForeignKeysError("Could not determine join " + "condition between parent/child tables on " + "relationship %s - there are multiple foreign key " + "paths linking the tables via secondary table '%s'. " + "Specify the 'foreign_keys' " + "argument, providing a list of those columns which " + "should be counted as containing a foreign key reference " + "from the secondary table to each of the parent and child tables." + % (self.prop, self.secondary)) + else: + raise sa_exc.AmbiguousForeignKeysError("Could not determine join " + "condition between parent/child tables on " + "relationship %s - there are multiple foreign key " + "paths linking the tables. Specify the 'foreign_keys' " + "argument, providing a list of those columns which " + "should be counted as containing a foreign key reference " + "to the parent table." + % self.prop) @util.memoized_property def primaryjoin_reverse_remote(self): @@ -515,7 +556,7 @@ class JoinCondition(object): err = "Could not locate any simple equality expressions "\ "involving foreign key columns for %s join condition "\ "'%s' on relationship %s." % ( - primary and 'primaryjoin' or 'secondaryjoin', + primary and 'primary' or 'secondary', join_condition, self.prop ) @@ -529,12 +570,12 @@ class JoinCondition(object): else: err = "Could not locate any relevant foreign key columns "\ "for %s join condition '%s' on relationship %s." % ( - primary and 'primaryjoin' or 'secondaryjoin', + primary and 'primary' or 'secondary', join_condition, self.prop ) - err += "Ensure that referencing columns are associated with a "\ - "a ForeignKey or ForeignKeyConstraint, or are annotated "\ + err += " Ensure that referencing columns are associated with a "\ + "ForeignKey or ForeignKeyConstraint, or are annotated "\ "in the join condition with the foreign() annotation." raise sa_exc.ArgumentError(err) diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 2862e9af92..38d95dde5d 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -284,8 +284,10 @@ def adapt_criterion_to_null(crit, nulls): return visitors.cloned_traverse(crit, {}, {'binary':visit_binary}) + def join_condition(a, b, ignore_nonexistent_tables=False, - a_subset=None): + a_subset=None, + consider_as_foreign_keys=None): """create a join condition between two tables or selectables. e.g.:: @@ -321,6 +323,9 @@ def join_condition(a, b, ignore_nonexistent_tables=False, for fk in sorted( b.foreign_keys, key=lambda fk:fk.parent._creation_order): + if consider_as_foreign_keys is not None and \ + fk.parent not in consider_as_foreign_keys: + continue try: col = fk.get_referent(left) except exc.NoReferenceError, nrte: @@ -336,6 +341,9 @@ def join_condition(a, b, ignore_nonexistent_tables=False, for fk in sorted( left.foreign_keys, key=lambda fk:fk.parent._creation_order): + if consider_as_foreign_keys is not None and \ + fk.parent not in consider_as_foreign_keys: + continue try: col = fk.get_referent(b) except exc.NoReferenceError, nrte: @@ -358,11 +366,11 @@ def join_condition(a, b, ignore_nonexistent_tables=False, "subquery using alias()?" else: hint = "" - raise exc.ArgumentError( + raise exc.NoForeignKeysError( "Can't find any foreign key relationships " "between '%s' and '%s'.%s" % (a.description, b.description, hint)) elif len(constraints) > 1: - raise exc.ArgumentError( + raise exc.AmbiguousForeignKeysError( "Can't determine join between '%s' and '%s'; " "tables have more than one foreign key " "constraint relationship between them. " diff --git a/test/orm/test_rel_fn.py b/test/orm/test_rel_fn.py index decf086d2f..9fe6ce1706 100644 --- a/test/orm/test_rel_fn.py +++ b/test/orm/test_rel_fn.py @@ -22,6 +22,12 @@ class _JoinFixtures(object): Column('x', Integer), Column('y', Integer), ) + cls.right_multi_fk = Table('rgt_multi_fk', m, + Column('id', Integer, primary_key=True), + Column('lid1', Integer, ForeignKey('lft.id')), + Column('lid2', Integer, ForeignKey('lft.id')), + ) + cls.selfref = Table('selfref', m, Column('id', Integer, primary_key=True), Column('sid', Integer, ForeignKey('selfref.id')) @@ -45,6 +51,16 @@ class _JoinFixtures(object): Column('lid', Integer, ForeignKey('m2mlft.id'), primary_key=True), Column('rid', Integer, ForeignKey('m2mrgt.id'), primary_key=True), ) + cls.m2msecondary_no_fks = Table('m2msecondary_no_fks', m, + Column('lid', Integer, primary_key=True), + Column('rid', Integer, primary_key=True), + ) + cls.m2msecondary_ambig_fks = Table('m2msecondary_ambig_fks', m, + Column('lid1', Integer, ForeignKey('m2mlft.id'), primary_key=True), + Column('rid1', Integer, ForeignKey('m2mrgt.id'), primary_key=True), + Column('lid2', Integer, ForeignKey('m2mlft.id'), primary_key=True), + Column('rid2', Integer, ForeignKey('m2mrgt.id'), primary_key=True), + ) cls.base_w_sub_rel = Table('base_w_sub_rel', m, Column('id', Integer, primary_key=True), Column('sub_id', Integer, ForeignKey('rel_sub.id')) @@ -302,6 +318,89 @@ class _JoinFixtures(object): fn ) + def _assert_raises_no_relevant_fks(self, fn, expr, relname, + primary, *arg, **kw): + assert_raises_message( + exc.ArgumentError, + r"Could not locate any relevant foreign key columns " + r"for %s join condition '%s' on relationship %s. " + r"Ensure that referencing columns are associated with " + r"a ForeignKey or ForeignKeyConstraint, or are annotated " + r"in the join condition with the foreign\(\) annotation." + % ( + primary, expr, relname + ), + fn, *arg, **kw + ) + + def _assert_raises_no_equality(self, fn, expr, relname, + primary, *arg, **kw): + assert_raises_message( + sa.exc.ArgumentError, + "Could not locate any simple equality expressions " + "involving foreign key columns for %s join " + "condition '%s' on relationship %s. " + "Ensure that referencing columns are associated with a " + "ForeignKey or ForeignKeyConstraint, or are annotated in " + r"the join condition with the foreign\(\) annotation. " + "To allow comparison operators other than '==', " + "the relationship can be marked as viewonly=True." % ( + primary, expr, relname + ), + fn, *arg, **kw + ) + + def _assert_raises_ambig_join(self, fn, relname, secondary_arg, + *arg, **kw): + if secondary_arg is not None: + assert_raises_message( + exc.AmbiguousForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship %s - " + "there are multiple foreign key paths linking the " + "tables via secondary table '%s'. " + "Specify the 'foreign_keys' argument, providing a list " + "of those columns which should be counted as " + "containing a foreign key reference from the " + "secondary table to each of the parent and child tables." + % (relname, secondary_arg), + fn, *arg, **kw) + else: + assert_raises_message( + exc.AmbiguousForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship %s - " + "there are no foreign keys linking these tables. " + % (relname,), + fn, *arg, **kw) + + def _assert_raises_no_join(self, fn, relname, secondary_arg, + *arg, **kw): + if secondary_arg is not None: + assert_raises_message( + exc.NoForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship %s - " + "there are no foreign keys linking these tables " + "via secondary table '%s'. " + "Ensure that referencing columns are associated with a ForeignKey " + "or ForeignKeyConstraint, or specify 'primaryjoin' and " + "'secondaryjoin' expressions" + % (relname, secondary_arg), + fn, *arg, **kw) + else: + assert_raises_message( + exc.NoForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship %s - " + "there are no foreign keys linking these tables. " + "Ensure that referencing columns are associated with a ForeignKey " + "or ForeignKeyConstraint, or specify a 'primaryjoin' " + "expression." + % (relname,), + fn, *arg, **kw) + + class ColumnCollectionsTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL): def test_determine_local_remote_pairs_o2o_joined_sub_to_base(self): joincond = self._join_fixture_o2o_joined_sub_to_base() @@ -398,13 +497,10 @@ class ColumnCollectionsTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL ) def test_err_local_remote_compound_1(self): - assert_raises_message( - exc.ArgumentError, - "Can't determine relationship direction for " - "relationship 'None' - foreign key " - "columns are present in neither the " - "parent nor the child's mapped tables", - self._join_fixture_compound_expression_1_non_annotated + self._assert_raises_no_relevant_fks( + self._join_fixture_compound_expression_1_non_annotated, + r'lft.x \+ lft.y = rgt.x \* rgt.y', + "None", "primary" ) def test_determine_remote_columns_compound_2(self): @@ -612,6 +708,80 @@ class DetermineJoinTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL): "lft.id = rgt.lid" ) + def test_determine_join_ambiguous_fks_o2m(self): + assert_raises_message( + exc.AmbiguousForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship None - " + "there are multiple foreign key paths linking " + "the tables. Specify the 'foreign_keys' argument, " + "providing a list of those columns which " + "should be counted as containing a foreign " + "key reference to the parent table.", + relationships.JoinCondition, + self.left, + self.right_multi_fk, + self.left, + self.right_multi_fk, + ) + + def test_determine_join_no_fks_o2m(self): + self._assert_raises_no_join( + relationships.JoinCondition, + "None", None, + self.left, + self.selfref, + self.left, + self.selfref, + ) + + + def test_determine_join_ambiguous_fks_m2m(self): + + self._assert_raises_ambig_join( + relationships.JoinCondition, + "None", self.m2msecondary_ambig_fks, + self.m2mleft, + self.m2mright, + self.m2mleft, + self.m2mright, + secondary=self.m2msecondary_ambig_fks + ) + + def test_determine_join_no_fks_m2m(self): + self._assert_raises_no_join( + relationships.JoinCondition, + "None", self.m2msecondary_no_fks, + self.m2mleft, + self.m2mright, + self.m2mleft, + self.m2mright, + secondary=self.m2msecondary_no_fks + ) + + def _join_fixture_fks_ambig_m2m(self): + return relationships.JoinCondition( + self.m2mleft, + self.m2mright, + self.m2mleft, + self.m2mright, + secondary=self.m2msecondary_ambig_fks, + consider_as_foreign_keys=[ + self.m2msecondary_ambig_fks.c.lid1, + self.m2msecondary_ambig_fks.c.rid1] + ) + + def test_determine_join_w_fks_ambig_m2m(self): + joincond = self._join_fixture_fks_ambig_m2m() + self.assert_compile( + joincond.primaryjoin, + "m2mlft.id = m2msecondary_ambig_fks.lid1" + ) + self.assert_compile( + joincond.secondaryjoin, + "m2mrgt.id = m2msecondary_ambig_fks.rid1" + ) + class AdaptedJoinTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL): __dialect__ = 'default' diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py index 266f7a924a..859aa5b4e0 100644 --- a/test/orm/test_relationships.py +++ b/test/orm/test_relationships.py @@ -13,7 +13,7 @@ from sqlalchemy.orm.interfaces import ONETOMANY, MANYTOONE, MANYTOMANY from test.lib.testing import eq_, startswith_, AssertsCompiledSQL, is_ from test.lib import fixtures from test.orm import _fixtures - +from sqlalchemy import exc class DependencyTwoParentTest(fixtures.MappedTest): """Test flush() when a mapper is dependent on multiple relationships""" @@ -879,7 +879,6 @@ class AmbiguousJoinInterpretedAsSelfRef(fixtures.MappedTest): subscriber_table = Table('subscriber', metadata, Column('id', Integer, primary_key=True, test_needs_autoincrement=True), - Column('dummy', String(10)) # to appease older sqlite version ) address_table = Table('address', @@ -2044,6 +2043,148 @@ class InvalidRemoteSideTest(fixtures.MappedTest): "mean to set remote_side on the many-to-one side ?", configure_mappers) +class AmbiguousFKResolutionTest(fixtures.MappedTest): + @classmethod + def define_tables(cls, metadata): + Table("a", metadata, + Column('id', Integer, primary_key=True) + ) + Table("b", metadata, + Column('id', Integer, primary_key=True), + Column('aid_1', Integer, ForeignKey('a.id')), + Column('aid_2', Integer, ForeignKey('a.id')), + ) + Table("atob", metadata, + Column('aid', Integer), + Column('bid', Integer), + ) + Table("atob_ambiguous", metadata, + Column('aid1', Integer, ForeignKey('a.id')), + Column('bid1', Integer, ForeignKey('b.id')), + Column('aid2', Integer, ForeignKey('a.id')), + Column('bid2', Integer, ForeignKey('b.id')), + ) + + @classmethod + def setup_classes(cls): + class A(cls.Basic): + pass + class B(cls.Basic): + pass + + def test_ambiguous_fks_o2m(self): + A, B = self.classes.A, self.classes.B + a, b = self.tables.a, self.tables.b + mapper(A, a, properties={ + 'bs':relationship(B) + }) + mapper(B, b) + assert_raises_message( + sa.exc.AmbiguousForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship A.bs - " + "there are multiple foreign key paths linking " + "the tables. Specify the 'foreign_keys' argument, " + "providing a list of those columns which " + "should be counted as containing a foreign " + "key reference to the parent table.", + sa.orm.configure_mappers + ) + + def test_with_fks_o2m(self): + A, B = self.classes.A, self.classes.B + a, b = self.tables.a, self.tables.b + mapper(A, a, properties={ + 'bs':relationship(B, foreign_keys=b.c.aid_1) + }) + mapper(B, b) + sa.orm.configure_mappers() + assert A.bs.property.primaryjoin.compare( + a.c.id==b.c.aid_1 + ) + eq_( + A.bs.property._calculated_foreign_keys, + set([b.c.aid_1]) + ) + + def test_with_pj_o2m(self): + A, B = self.classes.A, self.classes.B + a, b = self.tables.a, self.tables.b + mapper(A, a, properties={ + 'bs':relationship(B, primaryjoin=a.c.id==b.c.aid_1) + }) + mapper(B, b) + sa.orm.configure_mappers() + assert A.bs.property.primaryjoin.compare( + a.c.id==b.c.aid_1 + ) + eq_( + A.bs.property._calculated_foreign_keys, + set([b.c.aid_1]) + ) + + def test_with_annotated_pj_o2m(self): + A, B = self.classes.A, self.classes.B + a, b = self.tables.a, self.tables.b + mapper(A, a, properties={ + 'bs':relationship(B, primaryjoin=a.c.id==foreign(b.c.aid_1)) + }) + mapper(B, b) + sa.orm.configure_mappers() + assert A.bs.property.primaryjoin.compare( + a.c.id==b.c.aid_1 + ) + eq_( + A.bs.property._calculated_foreign_keys, + set([b.c.aid_1]) + ) + + def test_no_fks_m2m(self): + A, B = self.classes.A, self.classes.B + a, b, a_to_b = self.tables.a, self.tables.b, self.tables.atob + mapper(A, a, properties={ + 'bs':relationship(B, secondary=a_to_b) + }) + mapper(B, b) + assert_raises_message( + sa.exc.NoForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship A.bs - " + "there are no foreign keys linking these " + "tables via secondary table 'atob'. " + "Specify 'primaryjoin' and 'secondaryjoin' expressions.", + sa.orm.configure_mappers + ) + + def test_ambiguous_fks_m2m(self): + A, B = self.classes.A, self.classes.B + a, b, a_to_b = self.tables.a, self.tables.b, self.tables.atob_ambiguous + mapper(A, a, properties={ + 'bs':relationship(B, secondary=a_to_b) + }) + mapper(B, b) + assert_raises_message( + sa.exc.AmbiguousForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship A.bs - " + "there are multiple foreign key paths linking " + "the tables via secondary table 'atob_ambiguous'. " + "Specify the 'foreign_keys' argument, providing a " + "list of those columns which should be counted " + "as containing a foreign key reference from the " + "secondary table to each of the parent and child tables.", + sa.orm.configure_mappers + ) + + def test_with_fks_m2m(self): + A, B = self.classes.A, self.classes.B + a, b, a_to_b = self.tables.a, self.tables.b, self.tables.atob_ambiguous + mapper(A, a, properties={ + 'bs':relationship(B, secondary=a_to_b, + foreign_keys=[a_to_b.c.aid1, a_to_b.c.bid1]) + }) + mapper(B, b) + sa.orm.configure_mappers() class InvalidRelationshipEscalationTest(fixtures.MappedTest): @@ -2070,6 +2211,88 @@ class InvalidRelationshipEscalationTest(fixtures.MappedTest): class Bar(cls.Basic): pass + def _assert_raises_no_relevant_fks(self, fn, expr, relname, + primary, *arg, **kw): + assert_raises_message( + sa.exc.ArgumentError, + "Could not locate any relevant foreign key columns " + "for %s join condition '%s' on relationship %s. " + "Ensure that referencing columns are associated with " + "a ForeignKey or ForeignKeyConstraint, or are annotated " + r"in the join condition with the foreign\(\) annotation." + % ( + primary, expr, relname + ), + fn, *arg, **kw + ) + + def _assert_raises_no_equality(self, fn, expr, relname, + primary, *arg, **kw): + assert_raises_message( + sa.exc.ArgumentError, + "Could not locate any simple equality expressions " + "involving foreign key columns for %s join " + "condition '%s' on relationship %s. " + "Ensure that referencing columns are associated with a " + "ForeignKey or ForeignKeyConstraint, or are annotated in " + r"the join condition with the foreign\(\) annotation. " + "To allow comparison operators other than '==', " + "the relationship can be marked as viewonly=True." % ( + primary, expr, relname + ), + fn, *arg, **kw + ) + + def _assert_raises_ambig_join(self, fn, relname, secondary_arg, + *arg, **kw): + if secondary_arg is not None: + assert_raises_message( + exc.ArgumentError, + "Could not determine join condition between " + "parent/child tables on relationship %s - " + "there are multiple foreign key paths linking the " + "tables via secondary table '%s'. " + "Specify the 'foreign_keys' argument, providing a list " + "of those columns which should be counted as " + "containing a foreign key reference from the " + "secondary table to each of the parent and child tables." + % (relname, secondary_arg), + fn, *arg, **kw) + else: + assert_raises_message( + exc.ArgumentError, + "Could not determine join condition between " + "parent/child tables on relationship %s - " + "there are no foreign keys linking these tables. " + % (relname,), + fn, *arg, **kw) + + def _assert_raises_no_join(self, fn, relname, secondary_arg, + *arg, **kw): + if secondary_arg is not None: + assert_raises_message( + exc.NoForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship %s - " + "there are no foreign keys linking these tables " + "via secondary table '%s'. " + "Ensure that referencing columns are associated with a ForeignKey " + "or ForeignKeyConstraint, or specify 'primaryjoin' and " + "'secondaryjoin' expressions" + % (relname, secondary_arg), + fn, *arg, **kw) + else: + assert_raises_message( + exc.NoForeignKeysError, + "Could not determine join condition between " + "parent/child tables on relationship %s - " + "there are no foreign keys linking these tables. " + "Ensure that referencing columns are associated with a ForeignKey " + "or ForeignKeyConstraint, or specify a 'primaryjoin' " + "expression." + % (relname,), + fn, *arg, **kw) + def test_no_join(self): bars, Foo, Bar, foos = (self.tables.bars, self.classes.Foo, @@ -2080,10 +2303,9 @@ class InvalidRelationshipEscalationTest(fixtures.MappedTest): 'bars':relationship(Bar)}) mapper(Bar, bars) - assert_raises_message( - sa.exc.ArgumentError, - "Could not determine join condition between parent/child " - "tables on relationship", sa.orm.configure_mappers) + self._assert_raises_ambig_join(sa.orm.configure_mappers, + "Foo.bars", None + ) def test_no_join_self_ref(self): bars, Foo, Bar, foos = (self.tables.bars, @@ -2242,7 +2464,7 @@ class InvalidRelationshipEscalationTest(fixtures.MappedTest): sa.orm.configure_mappers) - def test_no_equated_self_ref(self): + def test_no_equated_self_ref_no_fks(self): bars, Foo, Bar, foos = (self.tables.bars, self.classes.Foo, self.classes.Bar, @@ -2253,13 +2475,12 @@ class InvalidRelationshipEscalationTest(fixtures.MappedTest): primaryjoin=foos.c.id>foos.c.fid)}) mapper(Bar, bars) - assert_raises_message( - sa.exc.ArgumentError, - "Could not determine relationship direction for primaryjoin " - "condition", - configure_mappers) + self._assert_raises_no_relevant_fks(configure_mappers, + "foos.id > foos.fid", "Foo.foos", "primary" + ) - def test_no_equated_self_ref(self): + + def test_no_equated_self_ref_no_equality(self): bars, Foo, Bar, foos = (self.tables.bars, self.classes.Foo, self.classes.Bar, @@ -2271,14 +2492,9 @@ class InvalidRelationshipEscalationTest(fixtures.MappedTest): foreign_keys=[foos.c.fid])}) mapper(Bar, bars) - assert_raises_message( - sa.exc.ArgumentError, - "Could not locate any foreign-key-equated, " - "locally mapped column pairs for primaryjoin " - "condition 'foos.id > foos.fid' on relationship " - "Foo.foos. For more relaxed rules on join " - "conditions, the relationship may be marked as viewonly=True.", - sa.orm.configure_mappers) + self._assert_raises_no_equality(configure_mappers, + "foos.id > foos.fid", "Foo.foos", "primary" + ) def test_no_equated_viewonly(self): bars, Bar, bars_with_fks, foos_with_fks, Foo, foos = (self.tables.bars,