Flask-Migrate Documentation

repository·main·Indexed 25 days ago

https://github.com/miguelgrinberg/flask-migrate

Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. It provides a command-line interface via the `flask db` group to initialize migration repositories, generate migration scripts, apply or revert changes, and manage database revisions. The library supports multi-database environments and provides integration for aioflask.

Tokens
3.4K
Snippets
6
Records
25
Agent score
78%

What's inside Flask-Migrate

  1. Manage database migrations with the flask db CLI

    main

    Flask-Migrate provides database operations via the flask db command group. Follow this workflow to manage your schema:

    1. Initialize migrations: Run flask db init to create the migrations folder. This is done once per project.
    2. Generate a migration script: Run flask db migrate to detect changes in your models and generate a migration script. Important: Review and edit the generated script manually, as Alembic may not detect all changes (e.g., it currently cannot detect indexes).
    3. Apply migrations: Run flask db upgrade to apply the migration scripts to the database.

    To sync a database on another system, pull the migrations folder from version control and run flask db upgrade.

  2. How multi-database migrations work in Flask-Migrate

    main

    In a multi-database Flask application, Flask-Migrate uses a specialized env.py template to manage migrations across multiple database engines (binds).

    Key behaviors include:

    1. Engine Discovery: The template automatically detects database binds via SQLALCHEMY_BINDS in your Flask config or through the migrate extension's internal bind_names.
    2. URL Configuration: It dynamically injects the correct sqlalchemy.url for each bind into the Alembic configuration at runtime.
    3. Offline Mode: When running in offline mode (e.g., generating SQL scripts), it produces individual .sql files for each database (e.g., default.sql, bind_name.sql).
    4. Online Mode: When running online, it attempts to manage transactions across all engines. If USE_TWOPHASE is set to True, it uses two-phase commits to ensure atomicity across multiple databases.
    5. Empty Migration Prevention: The template includes a process_revision_directives hook that detects if an autogenerate command resulted in no actual schema changes, preventing the creation of empty migration files.
  3. How multi-database migrations work in Aioflask

    main

    In an aioflask multi-database configuration, Flask-Migrate manages migrations across multiple database engines (binds) simultaneously.

    1. Engine Discovery: The environment identifies all database binds by checking current_app.config['SQLALCHEMY_BINDS'] or querying the migrate extension's bind_names.
    2. Connection Management: For each bind, an engine is retrieved using get_engine(bind_key). In online mode, these are managed asynchronously using await engine.connect().start().
    3. Transaction Coordination: The template supports running migrations across all databases within a single logical transaction block. If USE_TWOPHASE is enabled, it attempts to use two-phase commits to ensure atomicity across different database engines. If one migration fails, all active transactions are rolled back.
    4. Metadata Mapping: The get_metadata(bind) function ensures that the correct schema definitions are applied to the correct database bind during the migration process.
  4. Prevent empty autogenerated migration scripts

    main

    When using autogenerate, Alembic may sometimes create a new migration script even if no schema changes were detected. The flask-migrate template for aioflask includes a process_revision_directives callback to prevent this.

    This callback checks if the upgrade_ops are empty. If no changes are detected and autogenerate is enabled, it clears the directives list and logs 'No changes in schema detected.', preventing the creation of an empty migration file.

    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.')
  5. Enable autogenerate support in Alembic env.py

    main

    To enable Alembic's autogenerate feature (which detects changes in your SQLAlchemy models), you must provide the target_metadata object to the Alembic configuration. In the env.py template used by Flask-Migrate for aioflask, this is handled by the get_metadata() function which retrieves metadata from the migrate extension attached to the current_app.

    If you are manually customizing your env.py, ensure you import your models and set the target_metadata correctly. The template uses target_db.metadata or target_db.metadatas[None] to automatically find your models' metadata.

    # Example of how you would manually set metadata if not using the template's automation
    # from myapp import mymodel
    # target_metadata = mymodel.Base.metadata
  6. Enable autogenerate support in multi-database environments

    main
    To enable Alembic's autogenerate feature (which detects schema changes automatically), you must provide the target_metadata object in your env.py file. In a multi-database setup, you should use the get_metadata(bind) helper function to ensure the correct metadata is associated with each specific database bind during the migration process.
  7. Enable autogenerate support in Aioflask multi-database environments

    main

    To use Alembic's autogenerate feature (which detects schema changes automatically), you must provide the MetaData object for your models to the Alembic environment. In a multi-database setup, you should use the get_metadata(bind) helper to retrieve the correct metadata associated with a specific database bind.

    If you are manually configuring env.py, ensure you import your models and set the target_metadata appropriately. In the provided template, get_metadata handles retrieving metadata from target_db.metadatas or falling back to a manual scan of tables tagged with a bind_key.

    # Example of how to provide metadata for autogenerate support
    # from myapp import mymodel
    # target_metadata = mymodel.Base.metadata