I had run into an interesting (and slightly risky) Alembic behavior while evolving a SQLAlchemy schema, and I thought it was worth sharing for others working with migrations.
While adding a new optional foreign key column to an existing table, Alembic autogenerated the migration like this 👇
op.create_foreign_key(None, 'order', 'invoice', ['invoice_id'], ['id'])
The constraint name is None.
Looks harmless — but this causes a downgrade failure later.
Why?
Because:
1️⃣ The database still creates a real FK constraint name
2️⃣ Alembic has no record of that actual name
3️⃣ The autogenerated downgrade tries to drop None

At downgrade time Alembic can’t map:
“constraint named None”
to
“the actual FK constraint in the database”
This can fail — or worse — drop the wrong constraint if multiple FKs exist.
This usually happens when:
a new FK is added in a later migration (not during table creation)
the column is nullable / optional
and no naming convention is defined
Solution 1 — Add a Global Naming Convention (Recommended)
I updated the SQLAlchemy metadata to use a deterministic naming scheme for all constraints:

This makes Alembic generate stable names like:
fk_order_invoice_id_invoice
Now the migration becomes:

And downgrade drops the exact same name.
No surprises. No flaky downgrades. Migration history stays deterministic.
Solution 2 — Explicitly Name the FK in the Model (Case-by-Case Fix)
When I want tight control on a specific constraint, I can also name it directly:

This is especially useful when:
patching an existing migration
or when only a single FK needs stabilizing
🎯 Key Takeaways
Alembic may emit create_foreign_key(None, …) for newly added FKs
The DB generates a real constraint name — but Alembic doesn’t know it
Downgrades can fail or behave unpredictably
Always ensure FK names are deterministic
Either:
- define a global naming convention (best long-term approach)
or
- explicitly name the FK constraint in the model or migration
I’m sharing this because issues like this rarely show up until:
the schema gets larger
multiple FKs exist on one table
or a downgrade is actually needed
…and then it can become painful very fast 🙂
If you’ve run into similar Alembic quirks would love to hear your experience 👇
0
1
0