Commits

Mike Bayer committed 3f95be6

- [feature] An explicit error is raised when
a ForeignKeyConstraint() that was
constructed to refer to multiple remote tables
is first used. [ticket:2455]

  • Participants
  • Parent commits 3676eb6

Comments (0)

Files changed (3)

     column object itself, consistent with the behavior
     of label(column, None).  [ticket:2168]
 
+  - [feature] An explicit error is raised when
+    a ForeignKeyConstraint() that was
+    constructed to refer to multiple remote tables
+    is first used. [ticket:2455]
+
 - sqlite
   - [feature] the SQLite date and time types
     have been overhauled to support a more open

File lib/sqlalchemy/schema.py

                 schema = parenttable.metadata.schema
 
             if (len(m) == 1):
-                tname   = m.pop()
+                tname = m.pop()
             else:
                 colname = m.pop()
-                tname   = m.pop()
+                tname = m.pop()
 
             if (len(m) > 0):
                 schema = '.'.join(m)
                 raise exc.NoReferencedTableError(
                     "Foreign key associated with column '%s' could not find "
                     "table '%s' with which to generate a "
-                    "foreign key to target column '%s'" % (self.parent, tname, colname),
+                    "foreign key to target column '%s'" %
+                    (self.parent, tname, colname),
                     tname)
             table = Table(tname, parenttable.metadata,
                           mustexist=True, schema=schema)
 
+            if not hasattr(self.constraint, '_referred_table'):
+                self.constraint._referred_table = table
+            elif self.constraint._referred_table is not table:
+                raise exc.ArgumentError(
+                    'ForeignKeyConstraint on %s(%s) refers to '
+                    'multiple remote tables: %s and %s' % (
+                    parenttable,
+                    self.constraint._col_description,
+                    self.constraint._referred_table,
+                    table
+                ))
+
             _column = None
             if colname is None:
                 # colname is None in the case that ForeignKey argument
 
 
     @property
+    def _col_description(self):
+        return ", ".join(self._elements)
+
+    @property
     def columns(self):
         return self._elements.keys()
 
 
     def _set_parent(self, table):
         super(ForeignKeyConstraint, self)._set_parent(table)
+
         for col, fk in self._elements.iteritems():
             # string-specified column names now get
             # resolved to Column objects

File test/sql/test_metadata.py

         assert s1.c.a.references(t1.c.a)
         assert not s1.c.a.references(t1.c.b)
 
+    def test_invalid_composite_fk_check(self):
+        m = MetaData()
+        t1 = Table('t1', m, Column('x', Integer), Column('y', Integer),
+            ForeignKeyConstraint(['x', 'y'], ['t2.x', 't3.y'])
+        )
+        t2 = Table('t2', m, Column('x', Integer))
+        t3 = Table('t3', m, Column('y', Integer))
+
+        assert_raises_message(
+            exc.ArgumentError,
+            r"ForeignKeyConstraint on t1\(x, y\) refers to "
+                "multiple remote tables: t2 and t3",
+            t1.join, t2
+        )
+        assert_raises_message(
+            exc.ArgumentError,
+            r"ForeignKeyConstraint on t1\(x, y\) refers to "
+                "multiple remote tables: t2 and t3",
+            t1.join, t3
+        )
+
+        assert_raises_message(
+            exc.ArgumentError,
+            r"ForeignKeyConstraint on t1\(x, y\) refers to "
+                "multiple remote tables: t2 and t3",
+            schema.CreateTable(t1).compile
+        )
+
 class ColumnDefinitionTest(AssertsCompiledSQL, fixtures.TestBase):
     """Test Column() construction."""