foreign key to a non-table doesn't raise (correctly, consistently)

Issue #2883 resolved
Mike Bayer repo owner created an issue
from sqlalchemy import *

m = MetaData()
t1 = Table('t1', m, Column('x', Integer))
t1a = t1.select().alias()

t2 = Table('t2', m, Column('y', Integer, ForeignKey(t1a.c.x)))


print repr(t2)

in 0.8:

  File "/Users/classic/tmp/sa084/lib/sqlalchemy/schema.py", line 1337, in __repr__
    return "ForeignKey(%r)" % self._get_colspec()
  File "/Users/classic/tmp/sa084/lib/sqlalchemy/schema.py", line 1387, in _get_colspec
    return "%s.%s" % (_column.table.fullname, _column.key)
AttributeError: 'Alias' object has no attribute 'fullname'

in 0.9, that error would happen, but is blocked by this:

 File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/sql/schema.py", line 1635, in _set_table
    self.constraint._set_parent_with_dispatch(table)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/sql/base.py", line 171, in _set_parent_with_dispatch
    self._set_parent(parent)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/sql/schema.py", line 2444, in _set_parent
    self._validate_dest_table(table)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/sql/schema.py", line 2417, in _validate_dest_table
    table_keys = set([for elem in self._elements.values()](elem._table_key()))
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/sql/schema.py", line 1475, in _table_key
    return _column.table.key
AttributeError: 'Alias' object has no attribute 'key'

patch:

diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
index 7bf543a..ec1d430 100644
--- a/lib/sqlalchemy/sql/schema.py
+++ b/lib/sqlalchemy/sql/schema.py
@@ -1340,6 +1340,7 @@ class ForeignKey(SchemaItem):
         """

         self._colspec = column
+        self._setup_colspec_arg(column)

         # the linked ForeignKeyConstraint.
         # ForeignKey will create this when parent Column
@@ -1389,6 +1390,27 @@ class ForeignKey(SchemaItem):
                 )
         return self._schema_item_copy(fk)

+    def _setup_colspec_arg(self, _colspec):
+        if isinstance(self._colspec, util.string_types):
+            self._table_column = None
+            return
+        elif hasattr(self._colspec, '__clause_element__'):
+            _column = self._colspec.__clause_element__()
+        else:
+            _column = self._colspec
+
+        if not isinstance(_column, ColumnClause):
+            raise exc.ArgumentError(
+                    "String, Column, or Column-bound argument "
+                    "expected, got %r" % _column)
+        elif not isinstance(_column.table, (util.NoneType, TableClause)):
+            raise exc.ArgumentError(
+                    "ForeignKey received Column not bound "
+                    "to a Table, got: %r" % _column.table
+                )
+        self._table_column = _column
+
+
     def _get_colspec(self, schema=None):
         """Return a string based 'column specification' for this
         :class:`.ForeignKey`.
@@ -1398,16 +1420,25 @@ class ForeignKey(SchemaItem):

         """
         if schema:
-            return schema + "." + self.column.table.name + \
-                                    "." + self.column.key
-        elif isinstance(self._colspec, util.string_types):
+            _schema, tname, colname = self._column_tokens
+            return "%s.%s.%s" % (schema, tname, colname)
+        elif self._table_column is not None:
+            return "%s.%s" % (
+                    self._table_column.table.fullname, self._table_column.key)
+        else:
             return self._colspec
-        elif hasattr(self._colspec, '__clause_element__'):
-            _column = self._colspec.__clause_element__()
+
+
+    def _table_key(self):
+        if self._table_column is not None:
+            if self._table_column.table is None:
+                return None
+            else:
+                return self._table_column.table.key
         else:
-            _column = self._colspec
+            schema, tname, colname = self._column_tokens
+            return _get_table_key(tname, schema)

-        return "%s.%s" % (_column.table.fullname, _column.key)


     target_fullname = property(_get_colspec)
@@ -1460,20 +1491,6 @@ class ForeignKey(SchemaItem):
             schema = None
         return schema, tname, colname

-    def _table_key(self):
-        if isinstance(self._colspec, util.string_types):
-            schema, tname, colname = self._column_tokens
-            return _get_table_key(tname, schema)
-        elif hasattr(self._colspec, '__clause_element__'):
-            _column = self._colspec.__clause_element__()
-        else:
-            _column = self._colspec
-
-        if _column.table is None:
-            return None
-        else:
-            return _column.table.key
-
     def _resolve_col_tokens(self):
         if self.parent is None:
             raise exc.InvalidRequestError(

Comments (2)

  1. Log in to comment