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.
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 orderAutogenerated 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 initsets up themigrations/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
1. Why can't `db.create_all()` be relied on to evolve a production database schema?
2. What underlying library does Flask-Migrate use to perform schema migrations?
3. Why should autogenerated migrations always be manually reviewed?
4. Why should migrations run as an explicit deploy step rather than automatically inside every app worker's startup code?
5. What is the recommended strategy for dropping a column that's still used by the previous version of the app during a rolling deployment?
Was this page helpful?
You May Also Like
Flask-SQLAlchemy Basics
Learn how Flask-SQLAlchemy wires the SQLAlchemy ORM into a Flask app, from configuration to sessions.
Defining Models in Flask
Learn how to declare Flask-SQLAlchemy models with columns, types, constraints, and relationships.
Querying with Flask-SQLAlchemy
Master filtering, joining, and optimizing database queries using Flask-SQLAlchemy's query interface.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics