# Flask-Migrate ships a version ahead of its own tag and leans on Alembic missing index changes

> The extension puts Alembic behind four flask db commands and one environment variable. What the packaging metadata and the documentation say about release cadence, the declared Python floor, and the point where a generated script stops being correct.

**miguelgrinberg/Flask-Migrate** — SQLAlchemy database migrations for Flask applications using Alembic

- Repository: https://github.com/miguelgrinberg/Flask-Migrate
- Stars: 2,406 · Forks: 235
- Language: Python
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/miguelgrinberg-flask-migrate

## Alembic cannot detect indexes, so every generated script needs editing

The workflow has a step that most migration write-ups skip. After the generate command runs, the script it produces needs to be reviewed and edited, because Alembic currently does not detect every change made to the models, and is in particular currently unable to detect indexes.

```
$ flask db migrate
```

The generated file is a draft, not a record of what changed. An index added to a model can yield a migration with nothing in it, and the only signal is the diff you have been told to read. That makes review part of the process rather than a courtesy, and it leaves no trace afterwards: nothing in the database will later tell you that a schema change was skipped, because the script that would have made it was committed without it. The same paragraph asks for the finalized script to be added to version control, which is the step that turns the draft into history. No version of Alembic is named as the point where index detection arrives, so for anyone modelling with indexes this caveat has no expiry date in the project.

## The migrations folder is source, so another machine needs nothing else

Two instructions in this file are about version control rather than about the database. The first command creates the database, or enables migrations if the database already exists, and adds a `migrations` folder to the application:

```
$ flask db init
```

That folder has to be committed along with the rest of the source files, and once a generated script has been edited it needs committing too. Then comes the deployment instruction: to sync the database in another system, refresh the `migrations` folder from source control and run the upgrade command. That is the entire transfer mechanism. No dump, no import, and no schema comparison appears anywhere in the file. A second environment takes its structure from the scripts rather than from a copy of the data, which means both environments have to agree on those scripts. If a developer edits a migration locally and forgets to push it, the next environment quietly applies something different. The pair of commands to run once is easy to remember, and their failure mode is not visible from the commands themselves.

## The version in the metadata is a dev string ahead of the newest tag

The build configuration names the distribution Flask-Migrate and gives its version as 4.1.1.dev0. The newest published release is v4.1.0, dated 2025-01-10, with v4.0.7 and v4.0.6 from March 2024 behind it. So the source tree sits one development increment past the last tag, and the last recorded push to the repository is 2026-08-29, around nineteen months after v4.1.0 was cut. Two things follow from that. Nobody installing from the index gets 4.1.1.dev0, because a development suffix marks a working tree rather than a distribution, so the code in the repository and the code you install differ by an increment that was never released. And the release line itself has stayed at 4.1.0 since the start of 2025 while commits kept arriving. For a library whose whole purpose is to be the stable layer between an application and its schema history, a version field that runs ahead of its own tags is worth reading before you pin anything.

## The declared floor is Python 3.6 with a dependency on Flask 0.9

The whole installation is one command:

```
pip install Flask-Migrate
```

The dependency block behind it states three requirements, and each one sets a floor well below what the rest of the metadata assumes. Flask >= 0.9, Flask-SQLAlchemy >= 1.0, alembic >= 1.9.0. Above them, requires-python is >=3.6, and the classifier list names only Programming Language :: Python :: 3, with no individual versions enumerated, so the package declares a floor without saying which releases above it are exercised. Alembic is the exception, carrying the only modern floor in the list. The Flask floor is the striking one, since it sits at a release from the early 2010s while the same file requires setuptools >= 61.2 to build, a tool version from a much later era. Read as a compatibility promise, the block declines to raise its floors as its own build tooling moves forward. The cost is practical: the resolver will not stop you if your Flask predates the extension, so the failure arrives later as an import error or a signature mismatch rather than as a resolution problem. The licence itself is declared twice over, once as an MIT classifier and once as license text in the table form, which is a sign of a metadata file kept across two generations of packaging style.

## flask db init cannot run until FLASK_APP is set

Four commands carry the entire interface, and the first one has a precondition the file calls out separately. `flask db init` creates the database, or enables migrations if the database already exists, and it adds the `migrations` folder to the application. For that to work at all, the `FLASK_APP` environment variable must be set according to the Flask documentation, so the README points at another project's page for the step that gates its own first command. The remaining three are `flask db migrate` to generate a migration, `flask db upgrade` to apply it, and:

```
$ flask db --help
```

which is how you see all the commands that are available. Everything else under `flask db` is discovered at the terminal rather than read in advance, and nothing in this file enumerates it. That is a reasonable choice for a wrapper whose command set comes from the library it wraps, and an inconvenient one for anyone writing documentation about it, since the interface has to be reproduced by running it rather than quoted.

## The git links point at a Forgejo instance while the package metadata points at GitHub

The resource links at the bottom of the file do not all lead to the same host. The git link, the change log link and the actions badge all point at code.miguelgrinberg.com, and the repository carries a `.forgejo/` directory at its root, which is the self-hosted forge that address belongs to. The packaging metadata points somewhere else, with `[project.urls]` setting the Homepage to github.com/miguelgrinberg/flask-migrate and the bug tracker to the issues page on the same account. So there are two addresses for one project, and which one answers a question depends on which half of the repository you are reading. The change log is one of the links that lives on the Forgejo side, while CHANGES.md also sits in the root listing, so the release history is reachable from either direction. Documentation is separate again, hosted at flask-migrate.readthedocs.io, with `.readthedocs.yaml` in the root as the configuration behind it.

## src layout with package data included and zip safety turned off

The build configuration is short enough to read in one pass. `package-dir` maps the empty key to `src`, and `packages.find` searches that directory with `namespaces = true`, so the package is discovered by scanning rather than named entry by entry. Two other settings matter to anyone building a wheel from this tree: `include-package-data = true` and `zip-safe = false`. The first means files tracked through packaging data travel with the distribution, and the root listing carries a MANIFEST.in to declare which. The second says the package is not installed as a zipped egg, which matters for anything that reads a file out of its own directory at runtime. The backend is setuptools.build_meta with a requirement of setuptools >= 61.2. The optional dependency groups are named for their use: `dev` collects tox, flake8 and pytest, `docs` collects sphinx. Both groups line up with entries at the root, tox.ini and tests for the first, docs and the readthedocs configuration for the second. The readme declared in that same file is README.md with a content type of text/markdown, matching the README.md in the root listing rather than the README.rst that projects of this age sometimes carry.

## Five donation platforms sit next to four commands

The closing section is longer than parts of the workflow above it. Support is asked for through GitHub Sponsors, Patreon, Buy me a Coffee, thanks.dev and PayPal, followed by a thank you. Above that sit the resource links: the forge, the change log, the documentation, PyPI, plus a contributor's guide, a security policy and a code of conduct that exist as files in the repository root. Set against four invocations, one of which exists only to print help, the proportions are worth naming rather than merely counting, since the sponsor list is how the maintainer of an extension this widely installed funds the work. What the layout does mean is that someone who has installed the package and wants to reverse the decision finds nothing here about removing it, about which Alembic version pairs with which release, or about what the security policy actually covers. The example application at the top of the file is the only place a schema appears, and it is six lines long: an app, a database URI pointing at a SQLite file, the Migrate object bound to both, and a single User model with an integer primary key and a 128 character name column. Everything else in the workflow is described in prose rather than shown against that model.

## Conclusion

Flask-Migrate suits applications that already use Flask-SQLAlchemy and want a schema history another machine can replay, and the cost of that is a review step you cannot automate away. Before pinning it, check which Alembic version you are on against the >=1.9.0 floor, read CHANGES.md for the release history rather than assuming the dev version in pyproject.toml matches what you installed, and remember that an index change will not appear in a generated script. Deploy by refreshing the migrations folder from source control and running upgrade, never by copying a database.

## FAQ

### how to install flask migrate

Install it with pip. All mandatory requirements are listed in pyproject.toml and cover Flask >= 0.9, Flask-SQLAlchemy >= 1.0 and alembic >= 1.9.0.

### what is flask migrate

Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are provided as command-line arguments under the `flask db` command.

### what does flask migrate do

It exposes Alembic through four Flask commands: `flask db init` to create the database or enable migrations and add the migrations folder, `flask db migrate` to generate a migration, `flask db upgrade` to apply it, and `flask db --help` to list what is available.

### how to use flask migrate

Set the `FLASK_APP` environment variable first, then run `flask db init`, followed by `flask db migrate` and `flask db upgrade`. Each time the models change, repeat the migrate and upgrade pair, and commit the migrations folder.

### what is flask migrate used for

Keeping a schema change history in source control. The migrations folder is committed with the application, and to sync another system you refresh that folder from source control and run the upgrade command.

## Sources

- [Issues](https://github.com/miguelgrinberg/Flask-Migrate/issues)
- [License: MIT](https://github.com/miguelgrinberg/Flask-Migrate/blob/main/LICENSE)
- [miguelgrinberg/Flask-Migrate on GitHub](https://github.com/miguelgrinberg/Flask-Migrate)
- [README](https://github.com/miguelgrinberg/Flask-Migrate/blob/main/README.md)
- [Releases](https://github.com/miguelgrinberg/Flask-Migrate/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/miguelgrinberg-flask-migrate
