如何在 alembic 迁移中禁用 DDL 事务

Jam*_*esC 3 python migration sqlalchemy alembic

我正在尝试运行 alembic 事务。但是,只要支持事务,所有迁移都会在事务中运行(请参阅在事务中运行 alembic 升级迁移)。如何禁用特定迁移的事务?

yoz*_*yoz 6

这可以使用自动提交块来完成:

with op.get_context().autocommit_block():
  op.execute(...)
Run Code Online (Sandbox Code Playgroud)

https://alembic.sqlalchemy.org/en/latest/api/runtime.html#alembic.runtime.migration.MigrationContext.autocommit_block

此特殊指令旨在支持偶尔必须在任何类型的事务块之外运行的数据库 DDL 或系统操作。PostgreSQL 数据库平台是这种操作方式最常见的目标,因为它的许多 DDL 操作必须在事务块之外运行,即使数据库整体支持事务性 DDL。

请注意,有一些警告:

警告:根据需要,块之前的数据库事务是无条件提交的。这意味着在整个迁移操作完成之前,将提交操作之前的迁移运行。建议当应用程序包含带有“自动提交”块的迁移时,EnvironmentContext.transaction_per_migration使用该块以便调用环境被调整为预期每个文件的短迁移,无论其中一个是否具有自动提交块。


Mar*_*ers 5

Alembic 过去只有两种使用事务的模式:

  • 整个迁移命令的一笔交易。如果要应用多个版本,则它们都在该单个事务中运行。
  • 每个迁移步骤使用单独的事务。

但是,从1.2.0 版(2019 年 9 月发布)开始,您现在还可以使用上下文管理器切换到AUTOCOMMIT事务级别。在这种事务模式下,每个语句都会立即提交。请注意,使用此功能有一些注意事项,请参见下文。MigrationContext.autocommit_block()

默认情况下使用单个事务,但您可以context.configure()env.py脚本中调用以将其设置transaction_per_migration为 true 以使用单独的事务。

第一个和默认选项,使用单个事务,在env.pyAlembic 为您生成的文件中执行,在该文件的run_migrations_online()函数中:

try:
    with context.begin_transaction():
        context.run_migrations()
finally:
    connection.close()
Run Code Online (Sandbox Code Playgroud)

您可以只编辑该文件以删除with context.begin_transaction():上下文管理器,也可以使用该context.get_x_argument()功能根据命令行开关切换事务:

try:
    # Python 3.7+
    from contextlib import nullcontext
except ImportError:
    # Earlier Python versions
    from contextlib import contextmanager
    @contextmanager
    def nullcontext():
        yield

# ...

def run_migrations_online():
    # ...
    if context.get_x_argument(as_dictionary=True).get('no-transaction', False):
        transaction_cm = nullcontext()
    else:
        transaction_cm = context.begin_transaction()
    try:
        with transaction_cm:
            context.run_migrations()
    finally:
        connection.close()
Run Code Online (Sandbox Code Playgroud)

要禁用每个迁移步骤或特定操作的事务,您可以使用上述autocommit_block(),它旨在用于数据库需要在事务上下文之外运行的 DDL 语句:

try:
    with context.begin_transaction():
        context.run_migrations()
finally:
    connection.close()
Run Code Online (Sandbox Code Playgroud)

上面的示例(取自文档)使用Operations.get_context()方法来访问迁移上下文。在上下文中,所有语句都直接执行,而不是在事务中运行。

需要注意的是,首先提交当前正在进行的任何事务。如果在这样的块之前和之后的语句是连接的并且不应该在没有其他语句的情况下执行,那么您要避免autocommit_block()在它们之间放置一个。您可能还想设置transaction_per_migration = true, 并autocommit_block()用于整个迁移步骤。这样,您至少可以最大限度地减少迁移步骤中途失败的问题。

在 1.2.0 版本之前,每个迁移步骤禁用事务并不容易。您必须完全禁用事务(只是不要使用context.begin_transaction()in env.py),然后在每个upgrade()downgrade()步骤中显式使用事务:

def run_migrations_online():
    # ...

    try:
        # no with context.begin_transaction() here
        context.run_migrations()
    finally:
        connection.close()
Run Code Online (Sandbox Code Playgroud)

并在每个迁移步骤中:

def upgrade():
    with context.begin_transaction():
        # ### commands auto generated by Alembic - please adjust! ###
        op.create_table(
            # ...
        )
        # etc.
Run Code Online (Sandbox Code Playgroud)