././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1611056199.0696704 Flask-Migrate-2.6.0/0000755000076600000240000000000000000000000015246 5ustar00mgrinbergstaff00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1611056199.0561113 Flask-Migrate-2.6.0/Flask_Migrate.egg-info/0000755000076600000240000000000000000000000021450 5ustar00mgrinbergstaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611056198.0 Flask-Migrate-2.6.0/Flask_Migrate.egg-info/PKG-INFO0000644000076600000240000000160100000000000022543 0ustar00mgrinbergstaff00000000000000Metadata-Version: 1.1 Name: Flask-Migrate Version: 2.6.0 Summary: SQLAlchemy database migrations for Flask applications using Alembic Home-page: http://github.com/miguelgrinberg/flask-migrate/ Author: Miguel Grinberg Author-email: miguelgrinberg50@gmail.com License: MIT Description: Flask-Migrate -------------- SQLAlchemy database migrations for Flask applications using Alembic. Platform: any Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611056198.0 Flask-Migrate-2.6.0/Flask_Migrate.egg-info/SOURCES.txt0000644000076600000240000000176600000000000023346 0ustar00mgrinbergstaff00000000000000LICENSE MANIFEST.in README.md __version__ setup.py Flask_Migrate.egg-info/PKG-INFO Flask_Migrate.egg-info/SOURCES.txt Flask_Migrate.egg-info/dependency_links.txt Flask_Migrate.egg-info/entry_points.txt Flask_Migrate.egg-info/not-zip-safe Flask_Migrate.egg-info/requires.txt Flask_Migrate.egg-info/top_level.txt flask_migrate/__init__.py flask_migrate/cli.py flask_migrate/templates/flask/README flask_migrate/templates/flask/alembic.ini.mako flask_migrate/templates/flask/env.py flask_migrate/templates/flask/script.py.mako flask_migrate/templates/flask-multidb/README flask_migrate/templates/flask-multidb/alembic.ini.mako flask_migrate/templates/flask-multidb/env.py flask_migrate/templates/flask-multidb/script.py.mako tests/__init__.py tests/app.py tests/app_compare_type1.py tests/app_compare_type2.py tests/app_custom_directory.py tests/app_custom_directory_path.py tests/app_multidb.py tests/test_migrate.py tests/test_migrate_flaskcli.py tests/test_multidb_migrate.py tests/test_multidb_migrate_flaskcli.py././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611056198.0 Flask-Migrate-2.6.0/Flask_Migrate.egg-info/dependency_links.txt0000644000076600000240000000000100000000000025516 0ustar00mgrinbergstaff00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611056198.0 Flask-Migrate-2.6.0/Flask_Migrate.egg-info/entry_points.txt0000644000076600000240000000005400000000000024745 0ustar00mgrinbergstaff00000000000000[flask.commands] db = flask_migrate.cli:db ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826849.0 Flask-Migrate-2.6.0/Flask_Migrate.egg-info/not-zip-safe0000644000076600000240000000000100000000000023676 0ustar00mgrinbergstaff00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611056198.0 Flask-Migrate-2.6.0/Flask_Migrate.egg-info/requires.txt0000644000076600000240000000005600000000000024051 0ustar00mgrinbergstaff00000000000000Flask>=0.9 Flask-SQLAlchemy>=1.0 alembic>=0.7 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611056198.0 Flask-Migrate-2.6.0/Flask_Migrate.egg-info/top_level.txt0000644000076600000240000000001600000000000024177 0ustar00mgrinbergstaff00000000000000flask_migrate ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826760.0 Flask-Migrate-2.6.0/LICENSE0000644000076600000240000000207200000000000016254 0ustar00mgrinbergstaff00000000000000The MIT License (MIT) Copyright (c) 2013 Miguel Grinberg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826760.0 Flask-Migrate-2.6.0/MANIFEST.in0000644000076600000240000000017100000000000017003 0ustar00mgrinbergstaff00000000000000include __version__ README.md LICENSE flask_migrate/templates/flask/* \ flask_migrate/templates/flask-multidb/* tests/* ././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1611056199.06939 Flask-Migrate-2.6.0/PKG-INFO0000644000076600000240000000160100000000000016341 0ustar00mgrinbergstaff00000000000000Metadata-Version: 1.1 Name: Flask-Migrate Version: 2.6.0 Summary: SQLAlchemy database migrations for Flask applications using Alembic Home-page: http://github.com/miguelgrinberg/flask-migrate/ Author: Miguel Grinberg Author-email: miguelgrinberg50@gmail.com License: MIT Description: Flask-Migrate -------------- SQLAlchemy database migrations for Flask applications using Alembic. Platform: any Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1605986906.0 Flask-Migrate-2.6.0/README.md0000644000076600000240000000443000000000000016526 0ustar00mgrinbergstaff00000000000000Flask-Migrate ============= [![Build status](https://github.com/miguelgrinberg/flask-migrate/workflows/build/badge.svg)](https://github.com/miguelgrinberg/flask-migrate/actions) Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are provided as command-line arguments under the `flask db` command. Installation ------------ Install Flask-Migrate with `pip`: pip install Flask-Migrate Example ------- This is an example application that handles database migrations through Flask-Migrate: ```python from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' db = SQLAlchemy(app) migrate = Migrate(app, db) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) ``` With the above application you can create the database or enable migrations if the database already exists with the following command: $ flask db init Note that the `FLASK_APP` environment variable must be set according to the Flask documentation for this command to work. This will add a `migrations` folder to your application. The contents of this folder need to be added to version control along with your other source files. You can then generate an initial migration: $ flask db migrate The migration script needs to be reviewed and edited, as Alembic currently does not detect every change you make to your models. In particular, Alembic is currently unable to detect indexes. Once finalized, the migration script also needs to be added to version control. Then you can apply the migration to the database: $ flask db upgrade Then each time the database models change repeat the `migrate` and `upgrade` commands. To sync the database in another system just refresh the `migrations` folder from source control and run the `upgrade` command. To see all the commands that are available run this command: $ flask db --help Resources --------- - [Documentation](http://flask-migrate.readthedocs.io/en/latest/) - [pypi](https://pypi.python.org/pypi/Flask-Migrate) - [Change Log](https://github.com/miguelgrinberg/Flask-Migrate/blob/master/CHANGES.md) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611056189.0 Flask-Migrate-2.6.0/__version__0000644000076600000240000000000600000000000017446 0ustar00mgrinbergstaff000000000000002.6.0 ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1611056199.057054 Flask-Migrate-2.6.0/flask_migrate/0000755000076600000240000000000000000000000020056 5ustar00mgrinbergstaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611055522.0 Flask-Migrate-2.6.0/flask_migrate/__init__.py0000755000076600000240000004363100000000000022201 0ustar00mgrinbergstaff00000000000000import argparse from functools import wraps import logging import os import sys from flask import current_app try: from flask_script import Manager except ImportError: Manager = None from alembic import __version__ as __alembic_version__ from alembic.config import Config as AlembicConfig from alembic import command from alembic.util import CommandError alembic_version = tuple([int(v) for v in __alembic_version__.split('.')[0:3]]) log = logging.getLogger(__name__) class _MigrateConfig(object): def __init__(self, migrate, db, **kwargs): self.migrate = migrate self.db = db self.directory = migrate.directory self.configure_args = kwargs @property def metadata(self): """ Backwards compatibility, in old releases app.extensions['migrate'] was set to db, and env.py accessed app.extensions['migrate'].metadata """ return self.db.metadata class Config(AlembicConfig): def get_template_directory(self): package_dir = os.path.abspath(os.path.dirname(__file__)) return os.path.join(package_dir, 'templates') class Migrate(object): def __init__(self, app=None, db=None, directory='migrations', **kwargs): self.configure_callbacks = [] self.db = db self.directory = str(directory) self.alembic_ctx_kwargs = kwargs if app is not None and db is not None: self.init_app(app, db, directory) def init_app(self, app, db=None, directory=None, **kwargs): self.db = db or self.db self.directory = str(directory or self.directory) self.alembic_ctx_kwargs.update(kwargs) if not hasattr(app, 'extensions'): app.extensions = {} app.extensions['migrate'] = _MigrateConfig( self, self.db, **self.alembic_ctx_kwargs) def configure(self, f): self.configure_callbacks.append(f) return f def call_configure_callbacks(self, config): for f in self.configure_callbacks: config = f(config) return config def get_config(self, directory=None, x_arg=None, opts=None): if directory is None: directory = self.directory directory = str(directory) config = Config(os.path.join(directory, 'alembic.ini')) config.set_main_option('script_location', directory) if config.cmd_opts is None: config.cmd_opts = argparse.Namespace() for opt in opts or []: setattr(config.cmd_opts, opt, True) if not hasattr(config.cmd_opts, 'x'): if x_arg is not None: setattr(config.cmd_opts, 'x', []) if isinstance(x_arg, list) or isinstance(x_arg, tuple): for x in x_arg: config.cmd_opts.x.append(x) else: config.cmd_opts.x.append(x_arg) else: setattr(config.cmd_opts, 'x', None) return self.call_configure_callbacks(config) def catch_errors(f): @wraps(f) def wrapped(*args, **kwargs): try: f(*args, **kwargs) except (CommandError, RuntimeError) as exc: log.error('Error: ' + str(exc)) sys.exit(1) return wrapped if Manager is not None: MigrateCommand = Manager(usage='Perform database migrations') else: class FakeCommand(object): def option(self, *args, **kwargs): def decorator(f): return f return decorator MigrateCommand = FakeCommand() @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @MigrateCommand.option('--multidb', dest='multidb', action='store_true', default=False, help=("Multiple databases migraton (default is " "False)")) @catch_errors def init(directory=None, multidb=False): """Creates a new migration repository""" if directory is None: directory = current_app.extensions['migrate'].directory config = Config() config.set_main_option('script_location', directory) config.config_file_name = os.path.join(directory, 'alembic.ini') config = current_app.extensions['migrate'].\ migrate.call_configure_callbacks(config) if multidb: command.init(config, directory, 'flask-multidb') else: command.init(config, directory, 'flask') @MigrateCommand.option('--rev-id', dest='rev_id', default=None, help=('Specify a hardcoded revision id instead of ' 'generating one')) @MigrateCommand.option('--version-path', dest='version_path', default=None, help=('Specify specific path from config for version ' 'file')) @MigrateCommand.option('--branch-label', dest='branch_label', default=None, help=('Specify a branch label to apply to the new ' 'revision')) @MigrateCommand.option('--splice', dest='splice', action='store_true', default=False, help=('Allow a non-head revision as the "head" to ' 'splice onto')) @MigrateCommand.option('--head', dest='head', default='head', help=('Specify head revision or @head to ' 'base new revision on')) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('--autogenerate', dest='autogenerate', action='store_true', default=False, help=('Populate revision script with candidate ' 'migration operations, based on comparison of ' 'database to model')) @MigrateCommand.option('-m', '--message', dest='message', default=None, help='Revision message') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @catch_errors def revision(directory=None, message=None, autogenerate=False, sql=False, head='head', splice=False, branch_label=None, version_path=None, rev_id=None): """Create a new revision file.""" config = current_app.extensions['migrate'].migrate.get_config(directory) command.revision(config, message, autogenerate=autogenerate, sql=sql, head=head, splice=splice, branch_label=branch_label, version_path=version_path, rev_id=rev_id) @MigrateCommand.option('--rev-id', dest='rev_id', default=None, help=('Specify a hardcoded revision id instead of ' 'generating one')) @MigrateCommand.option('--version-path', dest='version_path', default=None, help=('Specify specific path from config for version ' 'file')) @MigrateCommand.option('--branch-label', dest='branch_label', default=None, help=('Specify a branch label to apply to the new ' 'revision')) @MigrateCommand.option('--splice', dest='splice', action='store_true', default=False, help=('Allow a non-head revision as the "head" to ' 'splice onto')) @MigrateCommand.option('--head', dest='head', default='head', help=('Specify head revision or @head to ' 'base new revision on')) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('-m', '--message', dest='message', default=None) @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @MigrateCommand.option('-x', '--x-arg', dest='x_arg', default=None, action='append', help=("Additional arguments consumed " "by custom env.py scripts")) @catch_errors def migrate(directory=None, message=None, sql=False, head='head', splice=False, branch_label=None, version_path=None, rev_id=None, x_arg=None): """Alias for 'revision --autogenerate'""" config = current_app.extensions['migrate'].migrate.get_config( directory, opts=['autogenerate'], x_arg=x_arg) command.revision(config, message, autogenerate=True, sql=sql, head=head, splice=splice, branch_label=branch_label, version_path=version_path, rev_id=rev_id) @MigrateCommand.option('revision', nargs='?', default='head', help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @catch_errors def edit(directory=None, revision='current'): """Edit current revision.""" if alembic_version >= (0, 8, 0): config = current_app.extensions['migrate'].migrate.get_config( directory) command.edit(config, revision) else: raise RuntimeError('Alembic 0.8.0 or greater is required') @MigrateCommand.option('--rev-id', dest='rev_id', default=None, help=('Specify a hardcoded revision id instead of ' 'generating one')) @MigrateCommand.option('--branch-label', dest='branch_label', default=None, help=('Specify a branch label to apply to the new ' 'revision')) @MigrateCommand.option('-m', '--message', dest='message', default=None) @MigrateCommand.option('revisions', nargs='+', help='one or more revisions, or "heads" for all heads') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @catch_errors def merge(directory=None, revisions='', message=None, branch_label=None, rev_id=None): """Merge two revisions together. Creates a new migration file""" config = current_app.extensions['migrate'].migrate.get_config(directory) command.merge(config, revisions, message=message, branch_label=branch_label, rev_id=rev_id) @MigrateCommand.option('--tag', dest='tag', default=None, help=("Arbitrary 'tag' name - can be used by custom " "env.py scripts")) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('revision', nargs='?', default='head', help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @MigrateCommand.option('-x', '--x-arg', dest='x_arg', default=None, action='append', help=("Additional arguments consumed " "by custom env.py scripts")) @catch_errors def upgrade(directory=None, revision='head', sql=False, tag=None, x_arg=None): """Upgrade to a later version""" config = current_app.extensions['migrate'].migrate.get_config(directory, x_arg=x_arg) command.upgrade(config, revision, sql=sql, tag=tag) @MigrateCommand.option('--tag', dest='tag', default=None, help=("Arbitrary 'tag' name - can be used by custom " "env.py scripts")) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('revision', nargs='?', default="-1", help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @MigrateCommand.option('-x', '--x-arg', dest='x_arg', default=None, action='append', help=("Additional arguments consumed " "by custom env.py scripts")) @catch_errors def downgrade(directory=None, revision='-1', sql=False, tag=None, x_arg=None): """Revert to a previous version""" config = current_app.extensions['migrate'].migrate.get_config(directory, x_arg=x_arg) if sql and revision == '-1': revision = 'head:-1' command.downgrade(config, revision, sql=sql, tag=tag) @MigrateCommand.option('revision', nargs='?', default="head", help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @catch_errors def show(directory=None, revision='head'): """Show the revision denoted by the given symbol.""" config = current_app.extensions['migrate'].migrate.get_config(directory) command.show(config, revision) @MigrateCommand.option('-i', '--indicate-current', dest='indicate_current', action='store_true', default=False, help=('Indicate current version (Alembic 0.9.9 or ' 'greater is required)')) @MigrateCommand.option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='Use more verbose output') @MigrateCommand.option('-r', '--rev-range', dest='rev_range', default=None, help=('Specify a revision range; format is ' '[start]:[end]')) @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @catch_errors def history(directory=None, rev_range=None, verbose=False, indicate_current=False): """List changeset scripts in chronological order.""" config = current_app.extensions['migrate'].migrate.get_config(directory) if alembic_version >= (0, 9, 9): command.history(config, rev_range, verbose=verbose, indicate_current=indicate_current) else: command.history(config, rev_range, verbose=verbose) @MigrateCommand.option('--resolve-dependencies', dest='resolve_dependencies', action='store_true', default=False, help='Treat dependency versions as down revisions') @MigrateCommand.option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='Use more verbose output') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @catch_errors def heads(directory=None, verbose=False, resolve_dependencies=False): """Show current available heads in the script directory""" config = current_app.extensions['migrate'].migrate.get_config(directory) command.heads(config, verbose=verbose, resolve_dependencies=resolve_dependencies) @MigrateCommand.option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='Use more verbose output') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @catch_errors def branches(directory=None, verbose=False): """Show current branch points""" config = current_app.extensions['migrate'].migrate.get_config(directory) command.branches(config, verbose=verbose) @MigrateCommand.option('-v', '--verbose', dest='verbose', action='store_true', default=False, help='Use more verbose output') @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @catch_errors def current(directory=None, verbose=False): """Display the current revision for each database.""" config = current_app.extensions['migrate'].migrate.get_config(directory) command.current(config, verbose=verbose) @MigrateCommand.option('--tag', dest='tag', default=None, help=("Arbitrary 'tag' name - can be used by custom " "env.py scripts")) @MigrateCommand.option('--sql', dest='sql', action='store_true', default=False, help=("Don't emit SQL to database - dump to standard " "output instead")) @MigrateCommand.option('revision', default=None, help="revision identifier") @MigrateCommand.option('-d', '--directory', dest='directory', default=None, help=("Migration script directory (default is " "'migrations')")) @catch_errors def stamp(directory=None, revision='head', sql=False, tag=None): """'stamp' the revision table with the given revision; don't run any migrations""" config = current_app.extensions['migrate'].migrate.get_config(directory) command.stamp(config, revision, sql=sql, tag=tag) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611055554.0 Flask-Migrate-2.6.0/flask_migrate/cli.py0000644000076600000240000002244000000000000021201 0ustar00mgrinbergstaff00000000000000import click from flask.cli import with_appcontext from flask_migrate import init as _init from flask_migrate import revision as _revision from flask_migrate import migrate as _migrate from flask_migrate import edit as _edit from flask_migrate import merge as _merge from flask_migrate import upgrade as _upgrade from flask_migrate import downgrade as _downgrade from flask_migrate import show as _show from flask_migrate import history as _history from flask_migrate import heads as _heads from flask_migrate import branches as _branches from flask_migrate import current as _current from flask_migrate import stamp as _stamp @click.group() def db(): """Perform database migrations.""" pass @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('--multidb', is_flag=True, help=('Support multiple databases')) @with_appcontext def init(directory, multidb): """Creates a new migration repository.""" _init(directory, multidb) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('-m', '--message', default=None, help='Revision message') @click.option('--autogenerate', is_flag=True, help=('Populate revision script with candidate migration ' 'operations, based on comparison of database to model')) @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--head', default='head', help=('Specify head revision or @head to base new ' 'revision on')) @click.option('--splice', is_flag=True, help=('Allow a non-head revision as the "head" to splice onto')) @click.option('--branch-label', default=None, help=('Specify a branch label to apply to the new revision')) @click.option('--version-path', default=None, help=('Specify specific path from config for version file')) @click.option('--rev-id', default=None, help=('Specify a hardcoded revision id instead of generating ' 'one')) @with_appcontext def revision(directory, message, autogenerate, sql, head, splice, branch_label, version_path, rev_id): """Create a new revision file.""" _revision(directory, message, autogenerate, sql, head, splice, branch_label, version_path, rev_id) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('-m', '--message', default=None, help='Revision message') @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--head', default='head', help=('Specify head revision or @head to base new ' 'revision on')) @click.option('--splice', is_flag=True, help=('Allow a non-head revision as the "head" to splice onto')) @click.option('--branch-label', default=None, help=('Specify a branch label to apply to the new revision')) @click.option('--version-path', default=None, help=('Specify specific path from config for version file')) @click.option('--rev-id', default=None, help=('Specify a hardcoded revision id instead of generating ' 'one')) @click.option('-x', '--x-arg', multiple=True, help='Additional arguments consumed by custom env.py scripts') @with_appcontext def migrate(directory, message, sql, head, splice, branch_label, version_path, rev_id, x_arg): """Autogenerate a new revision file (Alias for 'revision --autogenerate')""" _migrate(directory, message, sql, head, splice, branch_label, version_path, rev_id, x_arg) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.argument('revision', default='head') @with_appcontext def edit(directory, revision): """Edit a revision file""" _edit(directory, revision) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('-m', '--message', default=None, help='Merge revision message') @click.option('--branch-label', default=None, help=('Specify a branch label to apply to the new revision')) @click.option('--rev-id', default=None, help=('Specify a hardcoded revision id instead of generating ' 'one')) @click.argument('revisions', nargs=-1) @with_appcontext def merge(directory, message, branch_label, rev_id, revisions): """Merge two revisions together, creating a new revision file""" _merge(directory, revisions, message, branch_label, rev_id) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--tag', default=None, help=('Arbitrary "tag" name - can be used by custom env.py ' 'scripts')) @click.option('-x', '--x-arg', multiple=True, help='Additional arguments consumed by custom env.py scripts') @click.argument('revision', default='head') @with_appcontext def upgrade(directory, sql, tag, x_arg, revision): """Upgrade to a later version""" _upgrade(directory, revision, sql, tag, x_arg) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--tag', default=None, help=('Arbitrary "tag" name - can be used by custom env.py ' 'scripts')) @click.option('-x', '--x-arg', multiple=True, help='Additional arguments consumed by custom env.py scripts') @click.argument('revision', default='-1') @with_appcontext def downgrade(directory, sql, tag, x_arg, revision): """Revert to a previous version""" _downgrade(directory, revision, sql, tag, x_arg) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.argument('revision', default='head') @with_appcontext def show(directory, revision): """Show the revision denoted by the given symbol.""" _show(directory, revision) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('-r', '--rev-range', default=None, help='Specify a revision range; format is [start]:[end]') @click.option('-v', '--verbose', is_flag=True, help='Use more verbose output') @click.option('-i', '--indicate-current', is_flag=True, help=('Indicate current version (Alembic 0.9.9 or greater is ' 'required)')) @with_appcontext def history(directory, rev_range, verbose, indicate_current): """List changeset scripts in chronological order.""" _history(directory, rev_range, verbose, indicate_current) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('-v', '--verbose', is_flag=True, help='Use more verbose output') @click.option('--resolve-dependencies', is_flag=True, help='Treat dependency versions as down revisions') @with_appcontext def heads(directory, verbose, resolve_dependencies): """Show current available heads in the script directory""" _heads(directory, verbose, resolve_dependencies) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('-v', '--verbose', is_flag=True, help='Use more verbose output') @with_appcontext def branches(directory, verbose): """Show current branch points""" _branches(directory, verbose) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('-v', '--verbose', is_flag=True, help='Use more verbose output') @with_appcontext def current(directory, verbose): """Display the current revision for each database.""" _current(directory, verbose) @db.command() @click.option('-d', '--directory', default=None, help=('Migration script directory (default is "migrations")')) @click.option('--sql', is_flag=True, help=('Don\'t emit SQL to database - dump to standard output ' 'instead')) @click.option('--tag', default=None, help=('Arbitrary "tag" name - can be used by custom env.py ' 'scripts')) @click.argument('revision', default='head') @with_appcontext def stamp(directory, sql, tag, revision): """'stamp' the revision table with the given revision; don't run any migrations""" _stamp(directory, revision, sql, tag) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1611056199.0516813 Flask-Migrate-2.6.0/flask_migrate/templates/0000755000076600000240000000000000000000000022054 5ustar00mgrinbergstaff00000000000000././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1611056199.05906 Flask-Migrate-2.6.0/flask_migrate/templates/flask/0000755000076600000240000000000000000000000023154 5ustar00mgrinbergstaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826760.0 Flask-Migrate-2.6.0/flask_migrate/templates/flask/README0000644000076600000240000000004600000000000024034 0ustar00mgrinbergstaff00000000000000Generic single-database configuration.././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826760.0 Flask-Migrate-2.6.0/flask_migrate/templates/flask/alembic.ini.mako0000644000076600000240000000140200000000000026174 0ustar00mgrinbergstaff00000000000000# 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1605987208.0 Flask-Migrate-2.6.0/flask_migrate/templates/flask/env.py0000644000076600000240000000554400000000000024326 0ustar00mgrinbergstaff00000000000000from __future__ import with_statement import logging from logging.config import fileConfig from sqlalchemy import engine_from_config from sqlalchemy import pool from flask import current_app from alembic import context # 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 config.set_main_option( 'sqlalchemy.url', str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) 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, target_metadata=target_metadata, literal_binds=True ) 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.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.', 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 ) with context.begin_transaction(): context.run_migrations() if context.is_offline_mode(): run_migrations_offline() else: run_migrations_online() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826760.0 Flask-Migrate-2.6.0/flask_migrate/templates/flask/script.py.mako0000644000076600000240000000075600000000000025770 0ustar00mgrinbergstaff00000000000000"""${message} Revision ID: ${up_revision} Revises: ${down_revision | comma,n} Create Date: ${create_date} """ from alembic import op import sqlalchemy as sa ${imports if imports else ""} # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} branch_labels = ${repr(branch_labels)} depends_on = ${repr(depends_on)} def upgrade(): ${upgrades if upgrades else "pass"} def downgrade(): ${downgrades if downgrades else "pass"} ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1611056199.0624595 Flask-Migrate-2.6.0/flask_migrate/templates/flask-multidb/0000755000076600000240000000000000000000000024612 5ustar00mgrinbergstaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826760.0 Flask-Migrate-2.6.0/flask_migrate/templates/flask-multidb/README0000644000076600000240000000004600000000000025472 0ustar00mgrinbergstaff00000000000000Generic single-database configuration.././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826760.0 Flask-Migrate-2.6.0/flask_migrate/templates/flask-multidb/alembic.ini.mako0000644000076600000240000000140200000000000027632 0ustar00mgrinbergstaff00000000000000# 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1605986906.0 Flask-Migrate-2.6.0/flask_migrate/templates/flask-multidb/env.py0000644000076600000240000001310400000000000025753 0ustar00mgrinbergstaff00000000000000from __future__ import with_statement import logging from logging.config import fileConfig from sqlalchemy import engine_from_config from sqlalchemy import MetaData from sqlalchemy import pool from flask import current_app from alembic import context USE_TWOPHASE = False # 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 config.set_main_option( 'sqlalchemy.url', str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) bind_names = [] for bind in current_app.config.get("SQLALCHEMY_BINDS"): context.config.set_section_option( bind, "sqlalchemy.url", str(current_app.extensions['migrate'].db.get_engine( current_app, bind).url).replace('%', '%%')) bind_names.append(bind) 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 get_metadata(bind): """Return the metadata for a bind.""" if bind == '': bind = None m = MetaData() for t in target_metadata.tables.values(): if t.info.get('bind_key') == bind: t.tometadata(m) return m 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. """ # for the --sql use case, run migrations for each URL into # individual files. engines = { '': { 'url': context.config.get_main_option('sqlalchemy.url') } } for name in bind_names: engines[name] = rec = {} rec['url'] = context.config.get_section_option(name, "sqlalchemy.url") for name, rec in engines.items(): logger.info("Migrating database %s" % (name or '')) file_ = "%s.sql" % name logger.info("Writing output to %s" % file_) with open(file_, 'w') as buffer: context.configure( url=rec['url'], output_buffer=buffer, target_metadata=get_metadata(name), literal_binds=True, ) with context.begin_transaction(): context.run_migrations(engine_name=name) 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.zzzcomputing.com/en/latest/cookbook.html def process_revision_directives(context, revision, directives): if getattr(config.cmd_opts, 'autogenerate', False): script = directives[0] if len(script.upgrade_ops_list) >= len(bind_names) + 1: empty = True for upgrade_ops in script.upgrade_ops_list: if not upgrade_ops.is_empty(): empty = False if empty: directives[:] = [] logger.info('No changes in schema detected.') # for the direct-to-DB use case, start a transaction on all # engines, then run all migrations, then commit all transactions. engines = { '': { 'engine': engine_from_config( config.get_section(config.config_ini_section), prefix='sqlalchemy.', poolclass=pool.NullPool, ) } } for name in bind_names: engines[name] = rec = {} rec['engine'] = engine_from_config( context.config.get_section(name), prefix='sqlalchemy.', poolclass=pool.NullPool) for name, rec in engines.items(): engine = rec['engine'] rec['connection'] = conn = engine.connect() if USE_TWOPHASE: rec['transaction'] = conn.begin_twophase() else: rec['transaction'] = conn.begin() try: for name, rec in engines.items(): logger.info("Migrating database %s" % (name or '')) context.configure( connection=rec['connection'], upgrade_token="%s_upgrades" % name, downgrade_token="%s_downgrades" % name, target_metadata=get_metadata(name), process_revision_directives=process_revision_directives, **current_app.extensions['migrate'].configure_args ) context.run_migrations(engine_name=name) if USE_TWOPHASE: for rec in engines.values(): rec['transaction'].prepare() for rec in engines.values(): rec['transaction'].commit() except: # noqa: E722 for rec in engines.values(): rec['transaction'].rollback() raise finally: for rec in engines.values(): rec['connection'].close() if context.is_offline_mode(): run_migrations_offline() else: run_migrations_online() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826760.0 Flask-Migrate-2.6.0/flask_migrate/templates/flask-multidb/script.py.mako0000644000076600000240000000170500000000000027421 0ustar00mgrinbergstaff00000000000000<%! import re %>"""${message} Revision ID: ${up_revision} Revises: ${down_revision | comma,n} Create Date: ${create_date} """ from alembic import op import sqlalchemy as sa ${imports if imports else ""} # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} branch_labels = ${repr(branch_labels)} depends_on = ${repr(depends_on)} def upgrade(engine_name): globals()["upgrade_%s" % engine_name]() def downgrade(engine_name): globals()["downgrade_%s" % engine_name]() <% from flask import current_app db_names = [''] + list(current_app.config.get("SQLALCHEMY_BINDS").keys()) %> ## generate an "upgrade_() / downgrade_()" function ## for each database name in the ini file. % for db_name in db_names: def upgrade_${db_name}(): ${context.get("%s_upgrades" % db_name, "pass")} def downgrade_${db_name}(): ${context.get("%s_downgrades" % db_name, "pass")} % endfor ././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1611056199.06976 Flask-Migrate-2.6.0/setup.cfg0000644000076600000240000000004600000000000017067 0ustar00mgrinbergstaff00000000000000[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826760.0 Flask-Migrate-2.6.0/setup.py0000644000076600000240000000256000000000000016763 0ustar00mgrinbergstaff00000000000000""" Flask-Migrate -------------- SQLAlchemy database migrations for Flask applications using Alembic. """ from setuptools import setup VERSION = open('__version__').read() setup( name='Flask-Migrate', version=VERSION, url='http://github.com/miguelgrinberg/flask-migrate/', license='MIT', author='Miguel Grinberg', author_email='miguelgrinberg50@gmail.com', description=('SQLAlchemy database migrations for Flask applications ' 'using Alembic'), long_description=__doc__, packages=['flask_migrate'], zip_safe=False, include_package_data=True, platforms='any', install_requires=[ 'Flask>=0.9', 'Flask-SQLAlchemy>=1.0', 'alembic>=0.7' ], tests_require=[ 'Flask-Script>=0.6' ], entry_points={ 'flask.commands': [ 'db=flask_migrate.cli:db' ], }, test_suite="tests", classifiers=[ 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules' ] ) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1611056199.0686085 Flask-Migrate-2.6.0/tests/0000755000076600000240000000000000000000000016410 5ustar00mgrinbergstaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1605986906.0 Flask-Migrate-2.6.0/tests/__init__.py0000644000076600000240000000000000000000000020507 0ustar00mgrinbergstaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1586016754.0 Flask-Migrate-2.6.0/tests/app.py0000755000076600000240000000123000000000000017541 0ustar00mgrinbergstaff00000000000000#!/bin/env python from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db) manager = Manager(app) manager.add_command('db', MigrateCommand) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) @manager.command def add(): db.session.add(User(name='test')) db.session.commit() if __name__ == '__main__': manager.run() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1605986906.0 Flask-Migrate-2.6.0/tests/app_compare_type1.py0000755000076600000240000000107500000000000022400 0ustar00mgrinbergstaff00000000000000from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db, compare_type=True) manager = Manager(app) manager.add_command('db', MigrateCommand) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) if __name__ == '__main__': manager.run() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1605986906.0 Flask-Migrate-2.6.0/tests/app_compare_type2.py0000755000076600000240000000107400000000000022400 0ustar00mgrinbergstaff00000000000000from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db, compare_type=True) manager = Manager(app) manager.add_command('db', MigrateCommand) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(10)) if __name__ == '__main__': manager.run() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1576105894.0 Flask-Migrate-2.6.0/tests/app_custom_directory.py0000755000076600000240000000125700000000000023230 0ustar00mgrinbergstaff00000000000000from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db, directory='temp_folder/temp_migrations') manager = Manager(app) manager.add_command('db', MigrateCommand) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) @manager.command def add(): db.session.add(User(name='test')) db.session.commit() if __name__ == '__main__': manager.run() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1584179853.0 Flask-Migrate-2.6.0/tests/app_custom_directory_path.py0000644000076600000240000000131600000000000024235 0ustar00mgrinbergstaff00000000000000from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand from pathlib import Path app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db, directory=Path('temp_folder/temp_migrations')) manager = Manager(app) manager.add_command('db', MigrateCommand) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) @manager.command def add(): db.session.add(User(name='test')) db.session.commit() if __name__ == '__main__': manager.run() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1576105740.0 Flask-Migrate-2.6.0/tests/app_multidb.py0000755000076600000240000000162000000000000021264 0ustar00mgrinbergstaff00000000000000#!/bin/env python from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app1.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_BINDS'] = { "db1": "sqlite:///app2.db", } db = SQLAlchemy(app) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) class Group(db.Model): __bind_key__ = 'db1' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) migrate = Migrate(app, db) manager = Manager(app) manager.add_command('db', MigrateCommand) @manager.command def add(): db.session.add(User(name='test')) db.session.add(Group(name='group')) db.session.commit() if __name__ == '__main__': manager.run() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1605988163.0 Flask-Migrate-2.6.0/tests/test_migrate.py0000644000076600000240000000676700000000000021471 0ustar00mgrinbergstaff00000000000000import os import shlex import shutil import subprocess import sys import unittest def run_cmd(cmd): """Run a command and return a tuple with (stdout, stderr, exit_code)""" print('\n$ ' + cmd) process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = process.communicate() print(stdout.decode('utf-8')) print(stderr.decode('utf-8')) return stdout, stderr, process.wait() class TestMigrate(unittest.TestCase): def setUp(self): os.chdir(os.path.split(os.path.abspath(__file__))[0]) try: os.remove('app.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass try: shutil.rmtree('temp_folder') except OSError: pass def tearDown(self): try: os.remove('app.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass try: shutil.rmtree('temp_folder') except OSError: pass def test_alembic_version(self): from flask_migrate import alembic_version self.assertEqual(len(alembic_version), 3) for v in alembic_version: self.assertTrue(isinstance(v, int)) def test_migrate_upgrade(self): (o, e, s) = run_cmd(sys.executable + ' app.py db init') self.assertTrue(s == 0) (o, e, s) = run_cmd(sys.executable + ' app.py db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd(sys.executable + ' app.py db upgrade') self.assertTrue(s == 0) (o, e, s) = run_cmd(sys.executable + ' app.py add') self.assertTrue(s == 0) def test_custom_directory(self): (o, e, s) = run_cmd( sys.executable + ' app_custom_directory.py db init') self.assertTrue(s == 0) (o, e, s) = run_cmd( sys.executable + ' app_custom_directory.py db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd( sys.executable + ' app_custom_directory.py db upgrade') self.assertTrue(s == 0) (o, e, s) = run_cmd(sys.executable + ' app_custom_directory.py add') self.assertTrue(s == 0) def test_custom_directory_path(self): (o, e, s) = run_cmd( sys.executable + ' app_custom_directory_path.py db init') self.assertTrue(s == 0) (o, e, s) = run_cmd( sys.executable + ' app_custom_directory_path.py db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd( sys.executable + ' app_custom_directory_path.py db upgrade') self.assertTrue(s == 0) (o, e, s) = run_cmd( sys.executable + ' app_custom_directory_path.py add') self.assertTrue(s == 0) def test_compare_type(self): (o, e, s) = run_cmd(sys.executable + ' app_compare_type1.py db init') self.assertTrue(s == 0) (o, e, s) = run_cmd( sys.executable + ' app_compare_type1.py db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd( sys.executable + ' app_compare_type1.py db upgrade') self.assertTrue(s == 0) (o, e, s) = run_cmd( sys.executable + ' app_compare_type2.py db migrate') self.assertTrue(s == 0) self.assertTrue(b'Detected type change from VARCHAR(length=128) ' b'to String(length=10)' in e) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1584179853.0 Flask-Migrate-2.6.0/tests/test_migrate_flaskcli.py0000644000076600000240000000650300000000000023325 0ustar00mgrinbergstaff00000000000000import os import shutil import unittest import subprocess import shlex def run_cmd(app, cmd): """Run a command and return a tuple with (stdout, stderr, exit_code)""" os.environ['FLASK_APP'] = app process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = process.communicate() print('\n$ ' + cmd) print(stdout.decode('utf-8')) print(stderr.decode('utf-8')) return stdout, stderr, process.wait() class TestMigrate(unittest.TestCase): def setUp(self): os.chdir(os.path.split(os.path.abspath(__file__))[0]) try: os.remove('app.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass try: shutil.rmtree('temp_folder') except OSError: pass def tearDown(self): try: os.remove('app.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass try: shutil.rmtree('temp_folder') except OSError: pass def test_alembic_version(self): from flask_migrate import alembic_version self.assertEqual(len(alembic_version), 3) for v in alembic_version: self.assertTrue(isinstance(v, int)) def test_migrate_upgrade(self): (o, e, s) = run_cmd('app.py', 'flask db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('app.py', 'flask db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('app.py', 'flask db upgrade') self.assertTrue(s == 0) from .app import db, User db.session.add(User(name='test')) db.session.commit() def test_custom_directory(self): (o, e, s) = run_cmd('app_custom_directory.py', 'flask db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_custom_directory.py', 'flask db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_custom_directory.py', 'flask db upgrade') self.assertTrue(s == 0) from .app_custom_directory import db, User db.session.add(User(name='test')) db.session.commit() def test_custom_directory_path(self): (o, e, s) = run_cmd('app_custom_directory_path.py', 'flask db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_custom_directory_path.py', 'flask db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_custom_directory_path.py', 'flask db upgrade') self.assertTrue(s == 0) from .app_custom_directory_path import db, User db.session.add(User(name='test')) db.session.commit() def test_compare_type(self): (o, e, s) = run_cmd('app_compare_type1.py', 'flask db init') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_compare_type1.py', 'flask db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_compare_type1.py', 'flask db upgrade') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_compare_type2.py', 'flask db migrate') self.assertTrue(s == 0) self.assertTrue(b'Detected type change from VARCHAR(length=128) ' b'to String(length=10)' in e) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1605986906.0 Flask-Migrate-2.6.0/tests/test_multidb_migrate.py0000644000076600000240000000536400000000000023201 0ustar00mgrinbergstaff00000000000000import os import shlex import shutil import sqlite3 import subprocess import sys import unittest def run_cmd(cmd): """Run a command and return a tuple with (stdout, stderr, exit_code)""" process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = process.communicate() return stdout, stderr, process.wait() class TestMigrate(unittest.TestCase): def setUp(self): os.chdir(os.path.split(os.path.abspath(__file__))[0]) try: os.remove('app1.db') os.remove('app2.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass def tearDown(self): try: os.remove('app1.db') os.remove('app2.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass def test_multidb_migrate_upgrade(self): (o, e, s) = run_cmd( sys.executable + ' app_multidb.py db init --multidb') self.assertTrue(s == 0) (o, e, s) = run_cmd(sys.executable + ' app_multidb.py db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd(sys.executable + ' app_multidb.py db upgrade') self.assertTrue(s == 0) # ensure the tables are in the correct databases conn1 = sqlite3.connect('app1.db') c = conn1.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn1.close() self.assertIn(('alembic_version',), tables) self.assertIn(('user',), tables) conn2 = sqlite3.connect('app2.db') c = conn2.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn2.close() self.assertIn(('alembic_version',), tables) self.assertIn(('group',), tables) # ensure the databases can be written to (o, e, s) = run_cmd(sys.executable + ' app_multidb.py add') self.assertTrue(s == 0) # ensure the downgrade works (o, e, s) = run_cmd(sys.executable + ' app_multidb.py db downgrade') self.assertTrue(s == 0) conn1 = sqlite3.connect('app1.db') c = conn1.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn1.close() self.assertIn(('alembic_version',), tables) self.assertNotIn(('user',), tables) conn2 = sqlite3.connect('app2.db') c = conn2.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn2.close() self.assertIn(('alembic_version',), tables) self.assertNotIn(('group',), tables) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1575826761.0 Flask-Migrate-2.6.0/tests/test_multidb_migrate_flaskcli.py0000644000076600000240000000543600000000000025051 0ustar00mgrinbergstaff00000000000000import os import shutil import unittest import subprocess import shlex import sqlite3 def run_cmd(app, cmd): """Run a command and return a tuple with (stdout, stderr, exit_code)""" os.environ['FLASK_APP'] = app process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = process.communicate() return stdout, stderr, process.wait() class TestMigrate(unittest.TestCase): def setUp(self): os.chdir(os.path.split(os.path.abspath(__file__))[0]) try: os.remove('app1.db') os.remove('app2.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass def tearDown(self): try: os.remove('app1.db') os.remove('app2.db') except OSError: pass try: shutil.rmtree('migrations') except OSError: pass def test_multidb_migrate_upgrade(self): (o, e, s) = run_cmd('app_multidb.py', 'flask db init --multidb') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_multidb.py', 'flask db migrate') self.assertTrue(s == 0) (o, e, s) = run_cmd('app_multidb.py', 'flask db upgrade') self.assertTrue(s == 0) # ensure the tables are in the correct databases conn1 = sqlite3.connect('app1.db') c = conn1.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn1.close() self.assertIn(('alembic_version',), tables) self.assertIn(('user',), tables) conn2 = sqlite3.connect('app2.db') c = conn2.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn2.close() self.assertIn(('alembic_version',), tables) self.assertIn(('group',), tables) # ensure the databases can be written to from .app_multidb import db, User, Group db.session.add(User(name='test')) db.session.add(Group(name='group')) db.session.commit() # ensure the downgrade works (o, e, s) = run_cmd('app_multidb.py', 'flask db downgrade') self.assertTrue(s == 0) conn1 = sqlite3.connect('app1.db') c = conn1.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn1.close() self.assertIn(('alembic_version',), tables) self.assertNotIn(('user',), tables) conn2 = sqlite3.connect('app2.db') c = conn2.cursor() c.execute('select name from sqlite_master') tables = c.fetchall() conn2.close() self.assertIn(('alembic_version',), tables) self.assertNotIn(('group',), tables)