>
dbwarden
/ compare / django migrations

dbwarden
vs Django Migrations.

Django proved that models should drive migrations. dbwarden brings that expectation to SQLAlchemy stacks without requiring Django or replacing SQL with Python operations.

ConcernDjangodbwarden
ScopeInside Django projectsAny SQLAlchemy stack
Schema sourceDjango modelsSQLAlchemy models
ArtifactPython operation modulesPlain SQL with rollback
State comparisonReconstructed from migration chainLive database, snapshots, or exported state
Data migrationsRunPython with historical modelsSQL only (manual files)

If you are building Django, use Django migrations. If you are building FastAPI or another SQLAlchemy service, dbwarden brings the model-driven workflow with plain SQL artifacts. The shape is the same: change a model, run makemigrations, inspect the generated migration, run migrate. dbwarden keeps that shape: change a SQLAlchemy model, run make-migrations, inspect the SQL, run migrate. That matters because many Python developers leave Django for FastAPI, Flask, Litestar, workers, or data services and find the model-driven workflow stayed behind.

Django migration
class Migration(migrations.Migration):
    dependencies = [("accounts", "0003_user")]
    operations = [
        migrations.AddField(
            model_name="user",
            name="bio",
            field=models.TextField(null=True),
        ),
    ]
dbwarden migration
-- upgrade
ALTER TABLE users ADD COLUMN bio TEXT;

-- rollback
ALTER TABLE users DROP COLUMN bio;

Django writes Python migration modules containing operation objects such as AddField and AlterField, and the framework translates them into backend SQL at execution time. RunPython is a genuine superpower for data migrations that need historical models. dbwarden writes the SQL itself. Upgrade and rollback sit in the same file, so the code review sees the exact statements that will execute. A DBA can run the file without Python, and the runner is optional rather than a deployment requirement.

Django's operation layer buys backend portability and Python data migrations. dbwarden's final-SQL artifact buys direct review: what you approve is what the database executes.

Django reconstructs state from the migration chain, then compares current models against that in-memory state. It is elegant and lets makemigrations work without a database, but it assumes the database matches the chain: an out-of-band production change can remain invisible until a later operation fails. dbwarden compares models against actual database state, checksummed schema snapshots, or exported model state. The next generation run exposes unexpected changes: the tool assumes hotfixes happen, because that is what production does.

$ python manage.py makemigrations
$ python manage.py migrate

$ dbwarden make-migrations "add bio"
$ dbwarden migrate --database primary

Django can ask interactively whether a disappeared field was renamed, and most operations know how to reverse themselves; RunPython needs an explicit reverse function. dbwarden makes renames explicit flags that work in CI and leave a trace, and generates rollback into the SQL file; a change that cannot be reversed must say so before it enters the repository.

Django migrations are integrated with Django's app registry, settings, test runner, admin, dependency graph, and ecosystem, which is why Django projects should use them. dbwarden requires SQLAlchemy and nothing else: FastAPI, Flask, Litestar, queue workers, scripts, and data pipelines can use it, and the official FastAPI plugin adds session dependencies and health routes while the core stays framework-independent.

dbwarden adds application-aware impact analysis, sandbox replay, offline state, safe type changes, and reverse engineering into SQLAlchemy models, and treats ClickHouse as a first-class backend alongside PostgreSQL, MySQL, MariaDB, and SQLite. Typed metadata is backend-aware: PGTableMeta covers partitioning, row-level security, fillfactor, and tablespaces; MyTableMeta covers engines, charsets, and row formats; CHTableMeta covers engine families, partitioning, ordering, TTLs, projections, materialized views, dictionaries, codecs, and skip indexes.

dbwarden check-db
dbwarden recover-model-state
dbwarden generate-models --base app.models.Base

Django has stronger integrated data migrations, app-scoped dependency graphs, and a mature test database experience. Those are real advantages. dbwarden is not trying to be Django outside Django; it is trying to make SQLAlchemy feel native in its own ecosystem.

Django reconstructs schema state by replaying its migration chain in memory, which makes every migration file load-bearing forever; squashmigrations exists to manage that growing chain. dbwarden keeps the same discipline: the convergence gate replays the full migration history against an empty database, so the SQL files remain load-bearing even though the models and checksummed state hold the authority.

State is stored as checksummed snapshots in .dbwarden/schemas/ and exported model state such as .dbwarden/model_state.primary.json. State files must be backed up and recovered carefully, since recover-model-state can only repair what a snapshot can express.

For an existing Django database, generate-models --base can create SQLAlchemy models, a baseline can mark the existing schema as adopted, and recover-model-state repairs missing or invalid offline state.

$ dbwarden generate-models --base app.models.Base
$ dbwarden make-migrations "baseline existing schema"
$ dbwarden migrate --baseline
$ dbwarden seed apply

Django projects should use Django migrations: they are integrated with the ORM, app registry, tests, admin, and ecosystem, and replacing them would be unnecessary complexity. RunPython with historical models, app-scoped migration dependencies, and the mature test database experience are real advantages. dbwarden targets the gap Django leaves: SQLAlchemy stacks outside Django, where final SQL and rollback should be visible together, multiple database engines share one workflow, and offline generation and application impact checks matter.

Start with dbwarden docs ↗