【问题标题】:Is there any way to generate sequential Revision IDs in Alembic?有没有办法在 Alembic 中生成顺序修订 ID?
【发布时间】:2019-04-17 15:07:56
【问题描述】:

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

alembic revision -m "adding a column"

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

从使事物可读的角度来看,这有点问题,因为这意味着当查看alembic/versions 目录时,所有文件将按随机顺序出现,而不是按顺序/时间顺序出现。

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

【问题讨论】:

    标签: sqlalchemy database-migration alembic


    【解决方案1】:

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

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

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

    这是来自文档的解释:

    file_template - 这是用于生成新文件的命名方案 迁移文件。存在的值是默认值,因此已注释 出去。可用的代币包括:

    • %%(rev)s - 修订 ID
    • %%(slug)s - 从修订消息派生的截断字符串
    • %%(年)d, %%(月).2d, %%(日).2d, %%(小时).2d, %%(分钟).2d, %%(秒).2d -创建日期的组成部分,默认为 datetime.datetime.now(),除非还使用了时区配置选项。

    因此将file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s 添加到alembic.ini 会将您的修订命名为2018-11-15_xxxxxxxxxxxx_adding_a_column.py

    我发现了这个问题:https://bitbucket.org/zzzeek/alembic/issues/371/add-unixtime-stamp-to-start-of-versions,它为我指明了正确的方向。

    A comment from from that issue:

    时间戳不一定告诉您哪个文件是最新的, 因为允许分支。 “alembic history”是最好的 这方面的真相来源。

    因此,文件命名解决方案不能保证迁移在目录中按逻辑排序(但会帮助 IMO)。可以提出相同的论点来反对使用顺序 ID。

    如果您确实想指定自己的修订标识符,请在命令行上使用 --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.langhelpers 的函数 rev_id()

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

    rev_id() 的函数调用在alembic 源中是硬编码的,因此如果没有对函数进行猴子修补,将很难覆盖该行为。您可以创建库的一个分支并重新定义该函数或使其调用的用于生成 id 的函数可配置。

    【讨论】:

    • 这是很棒的信息。谢谢你。这帮助我更容易解决我们经常遇到的问题。如果有人在检查另一个功能分支之前忘记降级,alembic history -i 将在找不到头部时爆炸。给日期加上前缀可以更容易地找到罪魁祸首。为 revid 加上时间戳的内置方法仍然是梦想,因为这实际上是 alembic_version 中的内容。我们开始使用--rev-id $(date -u +"%Y%m%dT%H%M%SZ")alembic revision
    【解决方案2】:

    我发现在我的情况下如何在没有额外的 bash 脚本的情况下做到这一点,只是 env.py 中的一些突变魔法。也许它会帮助某人。

    Alembic 具有 customizing generated revisions 的强大功能,因此我们可以在此级别编写覆盖:

    # 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

    【讨论】:

    • 很好的答案。为了添加修订号并保持原始 uuid 不受 alembic 影响,我修改了您的函数,如下所示:last_rev_id = int(head_revision[:4].lstrip("0"))migration_script.rev_id = f"{new_rev_id:04}_{uuid.uuid4().hex[-12:]}"
    【解决方案3】:

    我制作了一个脚本,根据####_ 模式已经存在多少迁移来自动增加修订号。这是一个 TLDR 版本。我将其保存为 migrations.sh 并更改第 2 行中的路径

    #!/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

    【讨论】:

      【解决方案4】:

      是的,但是使用日期和时间

      以下是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>
      

      【讨论】:

      • 这太棒了。我可以使用它,也可以批量重命名我的旧版本文件,看起来 alembic 仍然可以正常迁移。
      【解决方案5】:

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

      @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 命名方案,添加时间戳、日期等...

      【讨论】:

        猜你喜欢
        • 2020-11-17
        • 2019-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多