DH
3 min read

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:

# FastAPI / SQLAlchemy — read-only mirror of Django's auth_user
class User(Base):
__tablename__ = "auth_user"
__table_args__ = {"extend_existing": True}

id = Column(Integer, primary_key=True)
username = Column(String)
email = Column(String)

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:

# alembic_models.py
from sqlalchemy.orm import DeclarativeBase

class AlembicBase(DeclarativeBase):
pass

# Import only FastAPI-owned models here
from app.models.events import EventLog
from app.models.jobs import AsyncJob

In env.py:

from app.alembic_models import AlembicBase

target_metadata = AlembicBase.metadata

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:

# docker-compose
migrate:
build: .
command: >
sh -c "python manage.py migrate &&
alembic upgrade head"
depends_on:
db:
condition: service_healthy

One container, sequential execution. No parallelism.

Alternatively, use PostgreSQL advisory locks to coordinate across concurrent runners:

SELECT pg_try_advisory_lock(12345);
-- run migrations
SELECT pg_advisory_unlock(12345);

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:

# env.py
def include_object(object, name, type_, reflected, compare_to):
excluded_tables = {"django_migrations", "django_content_type",
"auth_permission", "django_admin_log"}
if type_ == "table" and name in excluded_tables:
return False
return True

context.configure(
...
include_object=include_object,
)

Rule #5: Test Migrations in Isolation

Both tools should verify migrations apply cleanly from scratch:

createdb test_schema_clean
DATABASE_URL=.../test_schema_clean python manage.py migrate
DATABASE_URL=.../test_schema_clean alembic upgrade head

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

Damian Hodgkiss

Senior Staff Engineer at Sumo Group, leading development of AppSumo marketplace. Technical solopreneur with 25+ years of experience building SaaS products.

Creating Freedom

Join me on the journey from engineer to solopreneur. Learn how to build profitable SaaS products while keeping your technical edge.

    Proven strategies

    Learn the counterintuitive ways to find and validate SaaS ideas

    Technical insights

    From choosing tech stacks to building your MVP efficiently

    Founder mindset

    Transform from engineer to entrepreneur with practical steps