有没有办法在Alembic中生成连续的修订ID?

47

我正在为一个Python项目使用Alembic作为数据库迁移工具。当我运行类似这样的命令时:

alembic revision -m "adding a column"

...它将添加一个名为alembic/versions/xxxxxxxxxxxx_adding_a_column.py的新文件,其中xxxxxxxxxxxx是随机生成的12位哈希值。

从可读性的角度来看,这有点棘手,因为这意味着在查看alembic/versions目录时,所有文件将以随机顺序而不是按照顺序/时间顺序出现。

Alembic中是否有任何选项可以确保这些前缀修订ID是连续的?我想我可以手动重命名文件,然后更新引用,但我想知道是否已经内置了这样的功能。

5个回答

77

听起来,您更感兴趣的是按顺序列出修订文件,而不是按顺序列出修订ID。前者可以在不改变修订ID生成方式的情况下实现。

运行alembic init alembic时生成的alembic.ini文件有一个配置修订文件命名的部分:

# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s

以下是来自文档的解释:

file_template - 这是用于生成新迁移文件的命名方案。当前值为默认值,已经被注释掉了。可用的记号包括:

  • %%(rev)s - 迁移标识符
  • %%(slug)s - 根据迁移消息生成的截断字符串
  • %%(year)d, %%(month).2d, %%(day).2d, %%(hour).2d, %%(minute).2d, %%(second).2d - 创建日期的组成部分,默认情况下为 datetime.datetime.now() ,除非还使用了时区配置选项。

因此,在 alembic.ini 中添加 file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s 将会把你的迁移命名为 2018-11-15_xxxxxxxxxxxx_adding_a_column.py

我在这个问题中找到了答案,它让我找到了正确的方向。

该问题的一个评论如下:

时间戳并不能保证告诉你哪个文件是最“新”的,因为允许分支。"alembic history" 应该是这方面的最佳信息源。

因此,文件命名方案不能保证迁移在目录中按逻辑顺序排序(但我认为会有所帮助)。同样的论点也可以用来反对使用连续标识符。

如果您确实想指定自己的迁移标识符,请在命令行上使用 --rev-id 标志。

例如:

alembic revision -m 'a message' --rev-id=1

将生成一个名为 1_a_message.py 的文件:

"""a message

Revision ID: 1
Revises:
Create Date: 2018-11-15 13:40:31.228888

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '1'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    pass


def downgrade():
    pass

你可以自己管理修订标识符。编写一个简单的bash脚本来触发修订生成将非常容易,自动传递基于日期时间的rev_id参数,例如 --rev-id=<current datetime>,以控制目录中列出的顺序。

如果未指定修订ID,则会调用位于alembic.util.langhelpersrev_id()函数:

def rev_id():
    return uuid.uuid4().hex[-12:]

在alembic源代码中,对rev_id()函数的调用是硬编码的,因此除非进行函数猴子补丁,否则很难覆盖其行为。您可以创建库的分支并重新定义该函数,或使它调用id生成的函数可配置。


6
这是非常棒的信息。谢谢。这帮助我更容易地解决了一个经常出现的问题。如果有人忘记降级就检出另一个特性分支,alembic history -i将无法找到头而崩溃。在日期前加上前缀可以更容易地定位罪犯。内置的时间戳方式仍然是梦想,因为它实际上是进入 alembic_version 的内容。我们开始使用 --rev-id $(date -u +"%Y%m%dT%H%M%SZ")alembic revision - John Christopher Jones
1
注意:v1.8将%%(epoch)s添加到file_template标记列表中。 - odigity
1
很遗憾,没有内置选项可以从1开始自动递增简单的整数。 - odigity

16
我在我的情况下发现了如何做到不需要额外的bash脚本,只需要在env.py中进行一些变异魔法。也许这对某些人有所帮助。
Alembic具有一个强大的功能,即自定义生成的修订版本,因此我们可以在此级别编写覆盖:
# env.py
def process_revision_directives(context, revision, directives):
    # extract Migration
    migration_script = directives[0]
    # extract current head revision
    head_revision = ScriptDirectory.from_config(context.config).get_current_head()
    
    if head_revision is None:
        # edge case with first migration
        new_rev_id = 1
    else:
        # default branch with incrementation
        last_rev_id = int(head_revision.lstrip('0'))
        new_rev_id = last_rev_id + 1
    # fill zeros up to 4 digits: 1 -> 0001
    migration_script.rev_id = '{0:04}'.format(new_rev_id)

...
# then use it context.configure
context.configure(
  ...
  process_revision_directives=process_revision_directives,
)

如果您希望将其用于没有使用 --autogenerate 选项创建的修订版本,您应该在 alembic.ini 中将 revision_environment 设置为 true。

1
非常好的答案。为了在Alembic中添加修订号并保留原始UUID,我已经修改了您的函数,代码如下:last_rev_id = int(head_revision [: 4] .lstrip(“0”))migration_script.rev_id = f"{new_rev_id:04}_{uuid.uuid4().hex[-12:]}". - Lionel Hamayon
1
快速提示,获取ScriptDirectory请执行以下操作: from alembic.script import ScriptDirectory - rmehlinger
很棒的答案!lstrip('0') 不是必需的。 - Levi
2
另外,如果您有多个“context.configure”,您应该将其添加到所有配置项中。 - Groosha

5
我编写了一个脚本,根据已经存在的与 ####_ 模式匹配的迁移数量自动递增修订号。这里是 TLDR 版本。如果你将其保存为 migrations.sh,并在第二行更改路径,则可以使用。
#!/usr/bin/env bash
NEXT_ID=`ls kennel/db/versions/* | grep -P '/\d{4}_.*\.py$' | wc -l`
alembic revision -m $@ --rev-id=`printf "%04d" ${NEXT_ID}`

然后,你可以像这样使用它:

./migrations.sh migration_name
# or 
./migrations.sh migration_name --autogenerate

完整脚本有文档并使用默认值 --autogenerate,可通过 --empty 标志禁用。 https://gist.github.com/chriscauley/cf0b038d055076a2a30de43526d4150e

2

虽然我不需要迁移分支,但我使用这个。

@writer.rewrites(ops.MigrationScript)
def revid_increment(ctx: migration.MigrationContext, revisions: tuple, op: ops.MigrationScript):
    op.rev_id = '{0:04}'.format(len(tuple(ctx.script.walk_revisions())) + 1)
    return op

这使得替换当前的rev_id命名方案、添加时间戳、日期等变得容易。


1

但是通过使用日期和时间

以下是alembic中可用的动态变量

file_template - 这是用于生成新迁移文件的命名方案。当前值为默认值,因此已注释。

可用令牌包括:

%%(rev)s - 修订ID
%%(slug)s - 从修订消息派生的截断字符串
%%(year)d, %%(month).2d, %%(day).2d, %%(hour).2d, %%(minute).2d, %%(second).2d - 按datetime.datetime.now()返回的创建日期的组件

例如,您可以使用以下配置进行顺序文件名

# template used to generate migration files
file_template = %%(year)d-%%(month).2d-%%(day).2d-%%(hour).2d-%%(minute).2d-%%(second).2d_%%(rev)s_%%(slug)s

这会生成以下输出。
YYYY-mm-dd-HH-MM-SS_<rev>_<message_slug>

2
这太棒了。我能够使用它,批量重命名我的旧版本文件,而且似乎alembic仍然能够正常迁移。 - DeusXMachina
@DeusXMachina “批量重命名”?听起来不错。Alembic提供这样的选项/工具吗? - jkulak
@jkulak 不好意思,我不得不自己写一些代码来完成这个任务。我基本上使用了一个Python脚本和一些Bash命令来获取每个文件的所有Git提交时间,并将其用于将文件重命名为Git提交日期(这足以接近代理)。然后我手动清理了任何冲突。 - DeusXMachina

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接