"""
colspec = fk._get_colspec()
- if metadata_schema is not None and colspec.count(".") == 1:
- # need to render schema breaking up tokens by hand, since the
- # ForeignKeyConstraint here may not actually have a remote
- # Table present
- # no schema in the colspec, render it
- colspec = "%s.%s" % (metadata_schema, colspec)
+ tokens = colspec.split(".")
+ tname, colname = tokens[-2:]
+
+ if metadata_schema is not None and len(tokens) == 2:
+ table_fullname = "%s.%s" % (metadata_schema, tname)
+ else:
+ table_fullname = ".".join(tokens[0:-1])
+
+ if fk.parent is not None and fk.parent.table is not None:
+ # try to resolve the remote table and adjust for column.key
+ parent_metadata = fk.parent.table.metadata
+ if table_fullname in parent_metadata.tables:
+ colname = _ident(parent_metadata.tables[table_fullname].c[colname].name)
+
+ colspec = "%s.%s" % (table_fullname, colname)
+
return colspec
"[%(refcols)s], %(args)s)" % {
"prefix": _sqlalchemy_autogenerate_prefix(autogen_context),
"cols": ", ".join(
- "%r" % f.parent.key for f in constraint.elements),
+ "%r" % _ident(f.parent.name) for f in constraint.elements),
"refcols": ", ".join(repr(_fk_colspec(f, apply_metadata_schema))
for f in constraint.elements),
"args": ", ".join(
.. changelog::
:version: 0.7.4
+ .. change::
+ :tags: bug, autogenerate
+ :tickets: 259
+
+ The rendering of a :class:`~sqlalchemy.schema.ForeignKeyConstraint`
+ will now ensure that the names of the source and target columns are
+ the database-side name of each column, and not the value of the
+ ``.key`` attribute as may be set only on the Python side.
+ This is because Alembic generates the DDL for constraints
+ as standalone objects without the need to actually refer to an in-Python
+ :class:`~sqlalchemy.schema.Table` object, so there's no step that
+ would resolve these Python-only key names to database column names.
+
.. change::
:tags: bug, autogenerate
:tickets: 260
from alembic import autogenerate, util, compat
from alembic.testing import eq_, eq_ignore_whitespace, config
+from alembic.testing.fixtures import op_fixture
+from alembic import op # noqa
+import sqlalchemy as sa # noqa
py3k = sys.version_info >= (3, )
"op.create_foreign_key('fk_a_id', 'b', 'a', ['a_id'], ['id'])"
)
+ def test_add_fk_constraint_inline_colkeys(self):
+ m = MetaData()
+ Table('a', m, Column('id', Integer, key='aid', primary_key=True))
+ b = Table(
+ 'b', m,
+ Column('a_id', Integer, ForeignKey('a.aid'), key='baid'))
+
+ py_code = autogenerate.render._add_table(b, self.autogen_context)
+
+ eq_ignore_whitespace(
+ py_code,
+ "op.create_table('b',"
+ "sa.Column('a_id', sa.Integer(), nullable=True),"
+ "sa.ForeignKeyConstraint(['a_id'], ['a.id'], ))"
+ )
+
+ context = op_fixture()
+ eval(py_code)
+ context.assert_(
+ "CREATE TABLE b (a_id INTEGER, "
+ "FOREIGN KEY(a_id) REFERENCES a (id))")
+
+ def test_add_fk_constraint_separate_colkeys(self):
+ m = MetaData()
+ Table('a', m, Column('id', Integer, key='aid', primary_key=True))
+ b = Table('b', m, Column('a_id', Integer, key='baid'))
+ fk = ForeignKeyConstraint(['baid'], ['a.aid'], name='fk_a_id')
+ b.append_constraint(fk)
+
+ py_code = autogenerate.render._add_table(b, self.autogen_context)
+
+ eq_ignore_whitespace(
+ py_code,
+ "op.create_table('b',"
+ "sa.Column('a_id', sa.Integer(), nullable=True),"
+ "sa.ForeignKeyConstraint(['a_id'], ['a.id'], name='fk_a_id'))"
+ )
+
+ context = op_fixture()
+ eval(py_code)
+ context.assert_(
+ "CREATE TABLE b (a_id INTEGER, CONSTRAINT "
+ "fk_a_id FOREIGN KEY(a_id) REFERENCES a (id))")
+
+ context = op_fixture()
+ py_code = autogenerate.render._add_fk_constraint(
+ fk, self.autogen_context)
+
+ eq_ignore_whitespace(
+ autogenerate.render._add_fk_constraint(fk, self.autogen_context),
+ "op.create_foreign_key('fk_a_id', 'b', 'a', ['a_id'], ['id'])"
+ )
+
+ eval(py_code)
+ context.assert_(
+ "ALTER TABLE b ADD CONSTRAINT fk_a_id "
+ "FOREIGN KEY(a_id) REFERENCES a (id)")
+
def test_add_fk_constraint_schema(self):
m = MetaData()
Table(