如何为 Alembic 编写可移植的布尔列

How to write portable Boolean columns for Alembic

我正在开发一个 Flask 应用程序,使用 Flask-Migrate(因此也使用 Alembic)来管理对数据库的更改。我 运行 很早就遇到了一个问题,即更改某些 not-nullable 布尔列会触发基础数据库中的错误。

我正在删除一个布尔列,并重命名另一列(相当于两次删除和一次添加)。

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('user', sa.Column('is_enabled', sa.Boolean(), nullable=False))
    op.drop_column('user', 'is_disabled')
    op.drop_column('user', 'is_deleted')
    # ### end Alembic commands ###

由于数据库中已有数据,因此添加需要一个默认值以用于现有行。但是,SQLAlchemy 人员 already passed 在布尔列中添加对 server_default 的(便携式)支持。

鉴于这些条件,是否有任何方法可以进行此迁移并保持数据库的可移植性,或者我是否需要将代码限制在一个数据库中,并开始根据底层数据库驱动程序编写 server_default 值布尔列?

是的,您可以更改升级(也可能还有降级)功能来实现您想要的。另请注意,您当前的升级不会保留现有数据,因为您正在添加新列并删除旧列。由于 alembic 无法检测到重命名,您需要更改升级(和降级)自动生成的函数。

要通过回填数据库实现您想要的效果:

  • Add/rename 没有 nullable 约束的列。
  • 当当前值为 Null 时,用默认值回填现有行。
  • 更改列以包含 nullable 约束:

要解决重命名问题,您需要将添加和删除行更改为 1 个更改行:

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.alter_column('user', 'is_disabled', new_column_name='is_enabled')
    op.execute('update user set is_disabled=false where is_disabled is null')
    op.alter_column('user', 'is_enabled', nullable=False)
    op.drop_column('user', 'is_deleted')
    # ### end Alembic commands ###

此外,当您将 is_disabled 更改为 is_enabled 时,您可以根据需要使用 op.execute 切换布尔值。