Database Migrations in a Django + FastAPI Shared-Schema Setup Without Conflicts
Run Django and FastAPI against one PostgreSQL schema without conflicts. Clear ownership rules, conflict detection, and working code patterns for shared migrations.
Database Migrations in a Django + FastAPI Shared-Schema Setup
Two frameworks, one database, zero drama—if you set it up right.
Running Django and FastAPI against the same PostgreSQL schema is common. Maybe Django handles your admin panel and background jobs while FastAPI serves a high-throughput read API. Maybe you're migrating incrementally. Either way, migrations become a minefield without clear ownership rules.
The Core Problem
Django's migration system assumes it owns the schema. It writes to django_migrations, tracks state in *.py files, and gets confused when anything else touches its tables.
FastAPI has no built-in migration system. You're typically using Alembic (SQLAlchemy) for migrations. Alembic also assumes schema ownership. When both systems try to own the same tables, you get conflicts and drift.
The fix is disciplined separation of concerns.
Rule #1: One Tool Owns Each Table
Pick Django or Alembic to own any given table. Never both.
A practical split:
- Django owns: auth, sessions, admin, Django app tables
- Alembic owns: event logs, API-specific entities, async job results
Enforce this at the code level. If FastAPI needs to read a Django-managed table, define it as a read-only SQLAlchemy model:
Keep this model out of Alembic's target metadata so Alembic never touches it.
Rule #2: Keep Alembic's Metadata Clean
Your Alembic env.py should only reference models Alembic owns. Use a separate metadata object:
In env.py:
Now alembic revision --autogenerate only sees FastAPI-owned tables.
Rule #3: Serialize Migration Execution
The biggest overlooked problem is concurrent migration execution during deploys. If python manage.py migrate and alembic upgrade head run simultaneously, you hit lock contention on ALTER TABLE operations.
Run migrations sequentially in your deploy pipeline. Django first, then Alembic:
One container, sequential execution. No parallelism.
Alternatively, use PostgreSQL advisory locks to coordinate across concurrent runners:
Wrap both Django and Alembic entrypoints with the same lock key.
Rule #4: Protect Django's Migration Table
FastAPI's SQLAlchemy setup should never touch django_migrations. Add it to Alembic's exclude list:
Rule #5: Test Migrations in Isolation
Both tools should verify migrations apply cleanly from scratch:
If this fails, you have a table conflict. Catch it in CI, not production.
The Tradeoff
This setup works but adds operational overhead: two migration histories, duplicate model definitions for shared tables, and a discipline contract across your team.
If you're early-stage, consider whether you actually need both frameworks. Sometimes Django alone or FastAPI with Alembic is simpler.
But when you genuinely need both, these rules keep the schema coherent.
Damian Hodgkiss
Senior Staff Engineer at Sumo Group, leading development of AppSumo marketplace. Technical solopreneur with 25+ years of experience building SaaS products.