This post will cover the minimal set-up to get basic (auto-generating) Alembic migrations working within a Flask application. The pre-requisites are; Flask, SQLAlchemy, Flask-SQLAlchemy and Alembic in case that wasn't obvious.
My current project is being built with Flask utilizing SQLalchemy as an abstraction to
my data persistence layer AKA ORM. I am also using the Flask-SQLALchemy
extension to make life even easier. In the fantastic Flask Mega Tutorial,
Miguel Grinburg uses sqlalchemy-migrate to manage the migrations of his database.
I found the code to be a bit heavy-handed and fragile, not to mention that sqlalchemy-migrate is incompatible with versions
of SQLAlchemy greater than 0.8, nor has it seen an update in sometime. The constant churn of packages and frameworks is a
riskfeature of Open Source Software. One has to be comfortable with this situation and I am!
That said, to the database migration rescue comes Alembic. This package is written by Mike Mayer who happens to be the father of SQLAlchemy itself. I can't think of another person who would be more qualified to write such a tool than he. We are a truly lucky lot in this case!
On with it now!
This first thing you need to do is create the migration environment. I suggest creating a virtualenv for your project before you start.
Once you have activated your virutalenv:
$ cd yourproject $ pip install alembic $ alembic init alembic
This command creates an Alembic directory and alembic.ini file in your project directory. Running
alembic init alembic
creates a generic configuration. Run it as described above unless you have a multi-database or a pylons project. The docs
for this are here.
Just like SQLAlchemy, I believe Alembic is customizable to its very core, but I am no expert. For my simple mission to get Alembic running, I chose to leave this file as is. I will revisit this aspect if conditions warrant in a future post.
This is where I made some modifications to the generated file to get it working with Flask.
from __future__ import with_statement import os, sys sys.path.append(os.getcwd()) from alembic import context from sqlalchemy import engine_from_config, pool from logging.config import fileConfig from <application> import app from <application>.models import db # This is the 'Generic' alembic modified with some settings # specific to this app # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config # Overwrite the sqlalchemy.url in the alembic.ini file. config.set_main_option('sqlalchemy.url', app.config['SQLALCHEMY_DATABASE_URI']) # Interpret the config file for Python logging. # This line sets up loggers basically. fileConfig(config.config_file_name) # add your model's MetaData object here # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata target_metadata = db.metadata
- Import os and sys to modify the path. I am not sure if this is a bug in Alembic, but I could not get Alembic to find my modules without appending the current working directory to my path.
- Import your app and db. Note: this is application specific as Flask will let you set these up in a few different places.
- Override the sqlalchemy.url in Alembic.ini file with a pointer to your applications database config
- Use db.metadata to provide automatic migration generation support.
$ alembic revision --autogenerate -m "<insert message here>"
You can inspect and modify the migration created by looking in the
Docs for this are here
$ alembic upgrade head
Docs for this are here.
... and we are good to go! Basic Alembic migrations should be running with most Flask apps. Flask is great framework for building python web apps. I plan on using it extensively for the next few projects floating around in my head. There will be a lot more on that front in due time. I will be sure to post more on my adventures with Flask, SQLAlchemy, Alembic and others as time permits...