100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Python

Database Migrations with Flask-Migrate

Manage evolving database schemas safely using Flask-Migrate's Alembic-backed migration workflow.

Data & PersistenceIntermediate9 min readJul 10, 2026
Analogies

Why Migrations Matter

db.create_all() is fine for a brand-new project because it only creates tables that don't yet exist — it never alters an existing table, so adding a column, renaming one, or changing a type on a live production database silently does nothing useful. Flask-Migrate solves this by wrapping Alembic, SQLAlchemy's migration toolkit, to generate versioned Python scripts that describe exactly how to transform the schema from one state to the next, and critically, how to reverse that transformation, giving you both upgrade() and downgrade() paths.

🏏

Cricket analogy: It is like the difference between laying a brand-new pitch before a series versus re-turfing sections of an existing Eden Gardens pitch mid-tournament without disturbing the parts already in play.

Setting Up the Migration Environment

After installing Flask-Migrate and wiring Migrate(app, db) alongside your db object, running flask db init creates a migrations/ directory containing Alembic's configuration and an env.py that knows how to reach your models' metadata. From there, flask db migrate -m "add user table" inspects the difference between your current models and the database's tracked state to autogenerate a migration script, and flask db upgrade actually applies it, while flask db downgrade reverts the most recent migration.

🏏

Cricket analogy: Running flask db init is like setting up the official scorebook format for a new domestic tournament before a single ball is bowled, establishing the record-keeping system everyone will use.

bash
flask db init                              # one-time setup, creates migrations/
flask db migrate -m "add email to user"    # autogenerate a migration script
flask db upgrade                           # apply pending migrations
flask db downgrade                         # revert the last migration
flask db history                           # list all migrations in order

Autogenerated migrations are a starting draft, not gospel — Alembic can't detect every change (like column renames, which it sees as a drop-and-add) or complex constraint changes, so always open the generated script and review it before running flask db upgrade against production.

Applying Migrations Safely in Production

In production, migrations should run as an explicit deployment step — typically flask db upgrade invoked once before the new application code starts serving traffic — rather than automatically inside app startup code, because running it from every worker process on every restart risks concurrent migration attempts racing against each other. For irreversible or risky changes, such as dropping a column that's still read by the previous app version during a rolling deploy, split the migration into multiple deploys: first add the new column and dual-write, then backfill data, then only in a later deploy drop the old column once nothing references it.

🏏

Cricket analogy: It is like scheduling a pitch relaying before a Test match rather than mid-innings — you do the disruptive work in a clean window, not while eleven fielders are mid-play and the ground crew would collide.

Never let db.create_all() and Flask-Migrate manage the same tables — once a table is under migration control, always evolve it through migration scripts, because mixing the two can leave Alembic's version tracking out of sync with the actual schema, causing future flask db migrate runs to generate incorrect diffs.

  • db.create_all() only creates missing tables; it never alters existing ones, which is why migrations are needed.
  • Flask-Migrate wraps Alembic to provide versioned, reversible schema change scripts.
  • flask db init sets up the migrations/ directory once per project.
  • flask db migrate -m "..." autogenerates a draft migration by diffing models against tracked schema state.
  • Always review autogenerated migrations before running flask db upgrade, especially for renames and constraint changes.
  • Run migrations as an explicit deploy step, not automatically from every app worker on startup.
  • Risky schema changes (like dropping a column) should be split across multiple deploys to support rolling updates.

Practice what you learned

Was this page helpful?

Topics covered

#Python#FlaskStudyNotes#WebDevelopment#DatabaseMigrationsWithFlaskMigrate#Database#Migrations#Flask#Migrate#SQL#StudyNotes#SkillVeris