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

时间:2018-12-05 22:44:54

标签: python sqlalchemy migration alembic

我正在尝试进行一次Alembic交易。但是,只要支持事务,所有迁移都将在事务中运行(请参见Run alembic upgrade migrations in a transaction)。如何为特定的迁移禁用交易?

2 个答案:

答案 0 :(得分:2)

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

with op.get_context().autocommit_block():
  op.execute(...)

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

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

请注意以下几点:

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

答案 1 :(得分:1)

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

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

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

使用单个事务的第一个也是默认选项是在Alembic为您生成的env.py文件中的run_migrations_online()函数中执行的:

try:
    with context.begin_transaction():
        context.run_migrations()
finally:
    connection.close()

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

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()

不幸的是,每个迁移步骤禁用交易并不容易。您会完全禁用事务(只是不要在context.begin_transaction()中使用env.py),然后在每个upgrade()downgrade()步骤中显式使用事务:

def run_migrations_online():
    # ...

    try:
        # no with context.begin_transaction() here
        context.run_migrations()
    finally:
        connection.close()

以及每个迁移步骤:

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