1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/ihatemoney_ynh.git synced 2024-09-03 19:26:15 +02:00

Handle migrations through alembic/flask-Migrations

Auto-initialization now applies migrations instead of using db. create_all()

fix #83
This commit is contained in:
Jocelyn Delande 2016-05-21 23:47:12 +02:00
parent 698efd7681
commit 74995f9959
7 changed files with 230 additions and 1 deletions

1
budget/migrations/README Executable file
View file

@ -0,0 +1 @@
Generic single-database configuration.

View file

@ -0,0 +1,45 @@
# A generic, single database configuration.
[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

85
budget/migrations/env.py Executable file
View file

@ -0,0 +1,85 @@
from __future__ import with_statement
from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
import logging
# This is the Alembic Config object, which provides access to the values within
# the .ini file in use.
config = context.config
# Interpret the config file for Python logging. This line sets up loggers
# basically.
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
# Add your model's MetaData object here for 'autogenerate' support from myapp
# import mymodel target_metadata = mymodel.Base.metadata.
from flask import current_app
config.set_main_option('sqlalchemy.url',
current_app.config.get('SQLALCHEMY_DATABASE_URI'))
target_metadata = current_app.extensions['migrate'].db.metadata
# Other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(url=url)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
# This callback is used to prevent an auto-migration from being generated
# when there are no changes to the schema.
# reference: http://alembic.readthedocs.org/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
logger.info('No changes in schema detected.')
engine = engine_from_config(config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool)
connection = engine.connect()
context.configure(connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
**current_app.extensions['migrate'].configure_args)
try:
with context.begin_transaction():
context.run_migrations()
finally:
connection.close()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

View file

@ -0,0 +1,22 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision}
Create Date: ${create_date}
"""
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View file

@ -0,0 +1,68 @@
"""Initial migration
Revision ID: b9a10d5d63ce
Revises: None
Create Date: 2016-05-21 23:21:21.605076
"""
# revision identifiers, used by Alembic.
revision = 'b9a10d5d63ce'
down_revision = None
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table('project',
sa.Column('id', sa.String(length=64), nullable=False),
sa.Column('name', sa.UnicodeText(), nullable=True),
sa.Column('password', sa.String(length=128), nullable=True),
sa.Column('contact_email', sa.String(length=128), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_table('archive',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('project_id', sa.String(length=64), nullable=True),
sa.Column('name', sa.UnicodeText(), nullable=True),
sa.ForeignKeyConstraint(['project_id'], ['project.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('person',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('project_id', sa.String(length=64), nullable=True),
sa.Column('name', sa.UnicodeText(), nullable=True),
sa.Column('activated', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['project_id'], ['project.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('bill',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('payer_id', sa.Integer(), nullable=True),
sa.Column('amount', sa.Float(), nullable=True),
sa.Column('date', sa.Date(), nullable=True),
sa.Column('what', sa.UnicodeText(), nullable=True),
sa.Column('archive', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['archive'], ['archive.id'], ),
sa.ForeignKeyConstraint(['payer_id'], ['person.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('billowers',
sa.Column('bill_id', sa.Integer(), nullable=True),
sa.Column('person_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['bill_id'], ['bill.id'], ),
sa.ForeignKeyConstraint(['person_id'], ['person.id'], )
)
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_table('billowers')
op.drop_table('bill')
op.drop_table('person')
op.drop_table('archive')
op.drop_table('project')
### end Alembic commands ###

View file

@ -2,6 +2,7 @@ flask>=0.9
flask-wtf==0.8 flask-wtf==0.8
flask-sqlalchemy flask-sqlalchemy
flask-mail>=0.8 flask-mail>=0.8
Flask-Migrate==1.8.0
flask-babel flask-babel
flask-rest flask-rest
jinja2==2.6 jinja2==2.6

View file

@ -3,6 +3,7 @@ import warnings
from flask import Flask, g, request, session from flask import Flask, g, request, session
from flask.ext.babel import Babel from flask.ext.babel import Babel
from flask.ext.migrate import Migrate, upgrade
from raven.contrib.flask import Sentry from raven.contrib.flask import Sentry
from web import main, db, mail from web import main, db, mail
@ -39,7 +40,13 @@ app.register_blueprint(api)
# db # db
db.init_app(app) db.init_app(app)
db.app = app db.app = app
db.create_all()
# db migrations
migrate = Migrate(app, db)
# auto-execute migrations on runtime
with app.app_context():
upgrade()
# mail # mail
mail.init_app(app) mail.init_app(app)