生成 SQL 脚本(又称“离线模式”)

生成 SQL 脚本(又称“离线模式”)#

Alembic 的一项主要功能是将迁移生成为 SQL 脚本,而不是在数据库上运行它们 - 这也称为离线模式。当在访问 DDL 受限的大型组织中工作时,这是一项关键功能,必须将 SQL 脚本交给 DBA。Alembic 通过传递给任何 upgradedowngrade 命令的 --sql 选项轻松实现这一点。例如,我们可以生成一个脚本,将修订版本修改为 ae1027a6acf

$ alembic upgrade ae1027a6acf --sql
INFO  [alembic.context] Context class PostgresqlContext.
INFO  [alembic.context] Will assume transactional DDL.
BEGIN;

CREATE TABLE alembic_version (
    version_num VARCHAR(32) NOT NULL
);

INFO  [alembic.context] Running upgrade None -> 1975ea83b712
CREATE TABLE account (
    id SERIAL NOT NULL,
    name VARCHAR(50) NOT NULL,
    description VARCHAR(200),
    PRIMARY KEY (id)
);

INFO  [alembic.context] Running upgrade 1975ea83b712 -> ae1027a6acf
ALTER TABLE account ADD COLUMN last_transaction_date TIMESTAMP WITHOUT TIME ZONE;

INSERT INTO alembic_version (version_num) VALUES ('ae1027a6acf');

COMMIT;

虽然日志记录配置转储到标准错误,但实际脚本转储到标准输出 - 因此在没有进一步配置(本节后面描述)的情况下,我们首先使用输出重定向来生成脚本

$ alembic upgrade ae1027a6acf --sql > migration.sql

获取开始版本#

请注意,我们的迁移脚本从基础开始 - 这是在离线模式下使用的默认设置,因为没有数据库连接,也没有 alembic_version 表可供读取。

在离线模式下提供起始版本的一种方法是向命令行提供一个范围。这是通过在 start:end 语法中提供“版本”来实现的

$ alembic upgrade 1975ea83b712:ae1027a6acf --sql > migration.sql

仅在离线模式下允许使用 start:end 语法;在“在线”模式下,始终使用 alembic_version 表来获取当前版本。

还可以让 env.py 脚本从本地环境(如本地文件)中检索“最后一个”版本。这种方案基本上会以 alembic_version 相同的方式处理本地文件

if context.is_offline_mode():
    version_file = os.path.join(os.path.dirname(config.config_file_name), "version.txt")
    if os.path.exists(version_file):
        current_version = open(version_file).read()
    else:
        current_version = None
    context.configure(dialect_name=engine.name, starting_rev=current_version)
    context.run_migrations()
    end_version = context.get_revision_argument()
    if end_version and end_version != current_version:
        open(version_file, 'w').write(end_version)

编写迁移脚本以支持脚本生成#

SQL 脚本生成面临的挑战是,我们生成的脚本不能依赖于任何客户端/服务器数据库访问。这意味着通过 SELECT 语句将一些行提取到内存中的迁移脚本在 --sql 模式下不起作用。同样重要的是,必须使用 Alembic 指令,它们全部专门设计为既可以在“实时执行”模式下工作,又可以在“离线 SQL 生成”模式下工作。

自定义环境#

鼓励 --sql 选项的用户根据自己的需要修改 env.py 文件。env.py 脚本按原样提供,分为两部分:run_migrations_online()run_migrations_offline()。运行哪个函数由脚本底部通过读取 EnvironmentContext.is_offline_mode() 来确定,该函数基本上确定是否启用了 --sql 标志。

例如,多个数据库配置可能希望遍历每个数据库并将迁移的输出设置为不同的命名文件 - EnvironmentContext.configure() 函数接受一个参数 output_buffer 以用于此目的。下面我们在 run_migrations_offline() 函数中对此进行了说明

from alembic import context
import myapp
import sys

db_1 = myapp.db_1
db_2 = myapp.db_2

def run_migrations_offline():
    """Run migrations *without* a SQL connection."""

    for name, engine, file_ in [
        ("db1", db_1, "db1.sql"),
        ("db2", db_2, "db2.sql"),
    ]:
        context.configure(
                    url=engine.url,
                    transactional_ddl=False,
                    output_buffer=open(file_, 'w'))
        context.execute("-- running migrations for '%s'" % name)
        context.run_migrations(name=name)
        sys.stderr.write("Wrote file '%s'" % file_)

def run_migrations_online():
    """Run migrations *with* a SQL connection."""

    for name, engine in [
        ("db1", db_1),
        ("db2", db_2),
    ]:
        connection = engine.connect()
        context.configure(connection=connection)
        try:
            context.run_migrations(name=name)
            session.commit()
        except:
            session.rollback()
            raise

if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()