Question

I'm using Alembic to handle migrations for Flask. alembic revision --autogenerate should, in theory, autogenerate a migration based on changes in my database. However, Alembic is simply generating a blank migration with the above command.

There's a question very similar to this one, where the issue was that the proper models weren't being imported. However, I have imported the models from my Flask app, as shown in env.py:

...
# import settings from Flask
alembic_config = config.get_section(config.config_ini_section)
from start import app
from models import User, Item, Recipient # models are imported here from models.py
alembic_config['sqlalchemy.url'] = app.config['SQLALCHEMY_DATABASE_URI']

engine = engine_from_config(
            alembic_config, # config.get_section(config.config_ini_section)
            prefix='sqlalchemy.',
            poolclass=pool.NullPool)
...

As well as imported db metadata in env.py ('start' is the name of my Flask app's main file):

...
from start import db
target_metadata = db.metadata
...

Running alembic revision --autogenerate -m "initial_rev" then generates an empty migration, although my Flask app would beg to differ:

"""initial_rev

Revision ID: 45296fd29540
Revises: None
Create Date: 2013-06-19 17:32:38.392268

"""

# revision identifiers, used by Alembic.
revision = '45296fd29540'
down_revision = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    pass
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    pass
    ### end Alembic commands ###

Edit

Here is a gist that shows the file structure for my app, as well as some additional code. It seems the issue is that Alembic does not like having the db imported from database.py without being initialized first in __init__.py. However, this is not possible when blueprints are used (because of cyclical imports), explained in this SO answer: https://stackoverflow.com/a/9695045/353878.

So the question is, how can Alembic be used when Flask blueprints are used as well?

Edit #2

I even tried printing db.metadata.sorted_tables, to make sure the database metadata was being imported correctly. Sure enough, the whole database schema was piped to the terminal. So why is Alembic generating blank upgrade/downgrade functions?

Edit #3

I've concluded that the issue has something to do with the differences of db.init_app(app) and db = SQLAlchemy(app), but I'm not quite sure what is causing the issue. To test this theory, I replaced from database import db in env.py to be db = SQLAlchemy(app). Probably a bad idea, but I wanted to see what would happen for debugging purposes.

Alembic autogenerated and filled the upgrade() and downgrade() methods- except they were reversed! upgrade() dropped all three of my tables, while downgrade() created them with all the proper columns and metadata. I have no idea why this is, but I hope it is helpful to people trying to figure out this problem.

Was it helpful?

Solution

Here is how I use Alembic with Flask and blueprints.

https://github.com/davidism/basic_flask

I use the application factory pattern and call db.init_app within that. After db = SQLAlchemy() I import all models that will subclass db.Model so that db.metadata is aware of them; note this is not done in the create_app factory, but just inline during module init.

When running alembic, the project folder is not included in sys.path so I set that up. Then I create an app from the factory and set sqlalchemy.url from its configuration. Also, I import db and set target_metadata = db.metadata.

This setup works for me all the time, no matter the project structure. I included a very basic set of user models and a very dumb view in a subpackage with a blueprint. Just make sure to load the relavent models in load_models, import the views after defining the blueprint, and import the blueprints in init_views.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top