From 7add04accc045e99177095e220a500faae682e46 Mon Sep 17 00:00:00 2001 From: Son Date: Thu, 14 Oct 2021 15:42:19 +0200 Subject: [PATCH] Use alembic instead of flask migrate which depends on flask-sqlalchemy --- CONTRIBUTING.md | 2 +- alembic.ini | 83 ++++++++++++++++++++++++++++++++++++++++ migrations/README | 1 + migrations/alembic.ini | 45 ---------------------- migrations/env.py | 43 ++++++++------------- scripts/new-migration.sh | 11 ++++-- 6 files changed, 109 insertions(+), 76 deletions(-) create mode 100644 alembic.ini create mode 100644 migrations/README delete mode 100644 migrations/alembic.ini diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c2f5b0cf..31c05847 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,7 +76,7 @@ docker run -e POSTGRES_PASSWORD=mypassword -e POSTGRES_USER=myuser -e POSTGRES_D To run the server: ``` -flask db upgrade && flask dummy-data && python3 server.py +alembic upgrade head && flask dummy-data && python3 server.py ``` then open http://localhost:7777, you should be able to login with `john@wick.com / password` account. diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 00000000..a8502487 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,83 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migrations + +# template used to generate migration files +file_template = %%(year)d_%%(month).2d%%(day).2d%%(hour).2d_%%(rev)s_%%(slug)s + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat alembic/versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks=black +# black.type=console_scripts +# black.entrypoint=black +# black.options=-l 79 + +# 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 diff --git a/migrations/README b/migrations/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini deleted file mode 100644 index b1b2d575..00000000 --- a/migrations/alembic.ini +++ /dev/null @@ -1,45 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# template used to generate migration files -file_template = %%(year)d_%%(month).2d%%(day).2d%%(hour).2d_%%(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 diff --git a/migrations/env.py b/migrations/env.py index 79b8174b..8ba3fcff 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -1,6 +1,3 @@ -from __future__ import with_statement - -import logging from logging.config import fileConfig from sqlalchemy import engine_from_config @@ -15,23 +12,28 @@ 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').replace('%', '%%')) -target_metadata = current_app.extensions['migrate'].db.metadata +import sys + +# hack to be able to import Base +# cf https://stackoverflow.com/a/58891735/1428034 +sys.path = ['', '..'] + sys.path[1:] + +from app.models import Base +from app.config import DB_URI +target_metadata = Base.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. +config.set_main_option('sqlalchemy.url', DB_URI) + def run_migrations_offline(): """Run migrations in 'offline' mode. @@ -47,7 +49,10 @@ def run_migrations_offline(): """ url = config.get_main_option("sqlalchemy.url") context.configure( - url=url, target_metadata=target_metadata, literal_binds=True + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, ) with context.begin_transaction(): @@ -61,29 +66,15 @@ def run_migrations_online(): 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.zzzcomputing.com/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.') - connectable = engine_from_config( config.get_section(config.config_ini_section), - prefix='sqlalchemy.', + prefix="sqlalchemy.", poolclass=pool.NullPool, ) with connectable.connect() as connection: context.configure( - connection=connection, - target_metadata=target_metadata, - process_revision_directives=process_revision_directives, - **current_app.extensions['migrate'].configure_args + connection=connection, target_metadata=target_metadata ) with context.begin_transaction(): diff --git a/scripts/new-migration.sh b/scripts/new-migration.sh index d2458117..4d41a08d 100644 --- a/scripts/new-migration.sh +++ b/scripts/new-migration.sh @@ -6,11 +6,14 @@ docker rm -f sl-db docker run -p 25432:5432 --name sl-db -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=sl -d postgres:13 -# run run `flask db upgrade` to upgrade the DB to the latest stage and -env DB_URI=postgresql://postgres:postgres@127.0.0.1:25432/sl poetry run flask db upgrade +# sleep a little bit for the db to be ready +sleep 3 -# finally `flask db migrate` to generate the migration script. -env DB_URI=postgresql://postgres:postgres@127.0.0.1:25432/sl poetry run flask db migrate +# upgrade the DB to the latest stage and +env DB_URI=postgresql://postgres:postgres@127.0.0.1:25432/sl poetry run alembic upgrade head + +# generate the migration script. +env DB_URI=postgresql://postgres:postgres@127.0.0.1:25432/sl poetry run alembic revision --autogenerate # remove the db docker rm -f sl-db \ No newline at end of file