2013-09-09T06:12:19Z
Flask-Migrate: Alembic database migration wrapper for Flask
In this post I introduce you to Flask-Migrate, a new database migration handler for Flask based on Alembic that I just made public.
Is a New Extension Necessary?
If you read the database chapter of my Mega-Tutorial, you know that I have chosen sqlalchemy-migrate for database migrations.
I liked sqlalchemy-migrate back then (I still do, actually), but its development appears to have halted completely. Support for SQLAlchemy 0.8.x has not been implemented yet, six months past the 0.8.0 release.
On the other side, since I wrote my migration Mega-Tutorial chapter Alembic has gained notoriety. Alembic is written by zzzeek (Mike Bayer), who is the author of SQLAlchemy. He is actively developing Alembic on bitbucket.
There is an extension called Flask-Alembic out there that has many similarities to mine, but that project also appears to have stalled, there haven't been any commits or messages from the developers in several months. The project was never made available on the Python Package Index (PyPI), so while it is possible to install directly from git, that is less ideal, and might be a deal breaker for some.
That is why I have decided to write Flask-Migrate. Out of respect for the Flask-Alembic project I decided to use a different name on PyPI, in case they ever decide to resume work on their project and publish it.
Using Flask-Migrate
Flask-Migrate provides a set of command line options that attach to Flask-Script.
To install the extension you use pip as usual:
$ pip install flask-migrate
As part of the installation you will also get Flask, Flask-SQLAlchemy and Flask-Script.
Below is a sample application that initializes Flask-Migrate and registers it with Flask-Script. As is typically the case with Flask-Script, the script is called manage.py:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.script import Manager
from flask.ext.migrate import Migrate, MigrateCommand
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(128))
if __name__ == '__main__':
manager.run()
When you run the application you get an additional db option in the command line (you can call it differently if you want, of course):
$ python manage.py --help
usage: manage.py [-h] {shell,db,runserver} ...
positional arguments:
{shell,db,runserver}
shell Runs a Python shell inside Flask application context.
db Perform database migrations
runserver Runs the Flask development server i.e. app.run()
optional arguments:
-h, --help show this help message and exit
The db command exposes most of the Alembic options:
$ python manage.py db --help
usage: Perform database migrations
positional arguments:
{upgrade,migrate,current,stamp,init,downgrade,history,revision}
upgrade Upgrade to a later version
migrate Alias for 'revision --autogenerate'
current Display the current revision for each database.
stamp 'stamp' the revision table with the given revision;
dont run any migrations
init Generates a new migration
downgrade Revert to a previous version
history List changeset scripts in chronological order.
revision Create a new revision file.
optional arguments:
-h, --help show this help message and exit
To add migration support to your database you just need to run the init command:
$ python manage.py db init
Creating directory /home/miguel/app/migrations...done
Creating directory /home/miguel/app/migrations/versions...done
Generating /home/miguel/app/alembic.ini...done
Generating /home/miguel/app/migrations/env.py...done
Generating /home/miguel/app/migrations/env.pyc...done
Generating /home/miguel/app/migrations/README...done
Generating /home/miguel/app/migrations/script.py.mako...done
Please edit configuration/connection/logging settings in
'/home/miguel/app/migrations/alembic.ini' before proceeding.
Note that you should replace manage.py with the name of your launch script if you used a different name.
When you use Alembic alone you have to edit a couple of configuration files, but Flask-Migrate handles all that for you. When the init command completes you will have a migrations folder with the configuration files ready to be used.
To issue your first migration you can run the following command:
$ python manage.py db migrate
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate] Detected added table 'user'
Generating /home/miguel/app/migrations/versions/4708a5190f2_.py...done
The migrate command adds a new migration script. You should review it and edit it to be accurate, as Alembic cannot detect all changes that you make to your models. In particular it does not detect indexes, so those need to be added manually to the script.
If you prefer to write your migration scripts from scratch then use revision instead of migrate:
$ python manage.py db revision
Generating /home/miguel/app/migrations/versions/15c04479d683_.py...done
You can read Alembic's documentation to learn how to write migration scripts.
The next step is to apply the migration to the database. For this you use the upgrade command:
$ python manage.py db upgrade
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
INFO [alembic.migration] Running upgrade None -> 4708a5190f2, empty message
And that's it! Your database is now synchronized with your models.
You should add all the files in the migrations folder to version control along with your source files. If you need to update another system to the latest database version you just need to update your source tree on that other system and then run db upgrade, like you did above.
If you have any suggestions to improve this extension please let me know below in the comments.
Miguel


#76 Robert said 2015-02-16T23:39:50Z
#77 Don Dwiggins said 2015-06-04T21:26:34Z
#78 Nana Okyere said 2015-07-06T02:32:18Z
#79 Miguel Grinberg said 2015-07-06T06:00:53Z
#80 Nana Okyere said 2015-07-06T17:44:06Z
#81 Nana Okyere said 2015-07-06T23:23:14Z
#82 Miguel Grinberg said 2015-07-07T01:53:55Z
#83 Nana Okyere said 2015-07-07T11:21:16Z
#84 Miguel Grinberg said 2015-07-07T21:55:05Z
#85 Nana Okyere said 2015-07-11T18:29:11Z
#86 Miguel Grinberg said 2015-07-16T05:59:09Z
#87 Alexey Shildyakov said 2015-07-29T05:58:02Z
#88 Miguel Grinberg said 2015-08-02T05:35:24Z
#89 Jeremmm said 2015-09-14T23:27:42Z
#90 Miguel Grinberg said 2015-09-15T17:58:16Z
#91 Nana Okyere said 2015-12-29T08:48:25Z
#92 Miguel Grinberg said 2016-01-03T22:51:02Z
#93 PJ said 2016-05-20T23:32:47Z
#94 Miguel Grinberg said 2016-05-22T00:39:02Z
#95 Mark said 2016-06-01T06:18:41Z
#96 Miguel Grinberg said 2016-06-02T02:36:41Z
#97 Mark said 2016-06-02T15:36:32Z
#98 Miguel Grinberg said 2016-06-05T06:02:11Z
#99 Michael said 2016-06-05T11:13:46Z
#100 Miguel Grinberg said 2016-06-05T18:19:01Z