2
votes

I am using SqlAlchemy and Flask-migrate for DB migration. I have successfully init the DB and upgrade once using the following:

python manage.py db init
python manage.py db migrate # creates the migration script,faefc6a6c7ae, as below:

"""empty message

Revision ID: faefc6a6c7ae
Revises: None
Create Date: 2016-10-25 22:09:25.615569

"""

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

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('users',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=100), nullable=False),
    sa.Column('email', sa.String(length=100), nullable=False),
    sa.Column('password', sa.String(length=100), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('posts',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('title', sa.String(length=50), nullable=False),
    sa.Column('description', sa.String(length=50), nullable=False),
    sa.Column('author_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('posts')
    op.drop_table('users')
    ### end Alembic commands ###

and then

python manage.py db upgrade # that actually does the migrations and creates the 'users' and the 'posts' tables in the db.

Now I run my test.py which has the following lines of code (apart from others):

from flask_testing import TestCase
class BaseTestCase(TestCase):
    def create_app(self):
        app.config.from_object('config.TestConfig')
        return app

    def setUp(self):
        db.create_all()
        db.session.add(BlogPost("Test post", "This is a test. Only a test."))
        db.session.add(User("admin", "[email protected]", "admin"))
        db.session.commit()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

Now the tearDown() drops all tables in the db after the test.py finishes running.

After this, now when I run

python manage.py db upgrade

it gives me the following messages on the console:

INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.

but does not really create the respective tables at all. I have a workaround though. So if I do a :

python manage.py db migrate # which creates a new migration script (not very different from the previous one) as below:

"""empty message

Revision ID: 848398c80589
Revises: faefc6a6c7ae
Create Date: 2016-10-26 00:06:26.468354

"""

# revision identifiers, used by Alembic.
revision = '848398c80589'
down_revision = 'faefc6a6c7ae'

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('users',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=100), nullable=False),
    sa.Column('email', sa.String(length=100), nullable=False),
    sa.Column('password', sa.String(length=100), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('posts',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('title', sa.String(length=50), nullable=False),
    sa.Column('description', sa.String(length=50), nullable=False),
    sa.Column('author_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('posts')
    op.drop_table('users')
    ### end Alembic commands ###

and then if I do

python manage.py db upgrade

again, the tables are created.

Now I would like to understand why were the tables not created when

python manage.py db upgrade

was run the first time itself ? The 2 migration scripts created by db migrate are not really much different or are they? So, why won't the upgrade work the first time itself (when the upgrade actually has code that creates the tables) and why would it work perfectly when the new script is generated again (where the upgrade method itself has not changed at all as compared to the previous migrate script ?

1

1 Answers

2
votes

drop_tables only drops the tables it knows about. Alembic adds its own table, alembic_version, to keep track of what migration the database is at. Since that table isn't dropped by drop_tables, Alembic still sees it with the value it set after running the upgrades. Run the downgrade command instead to go back to the empty initial state.

from flask_migrate import downgrade

downgrade(revision='base')

However, you should not be downgrading just to run tests, you should use a separate database so that you don't mess with development or production data.