自动生成迁移时如何使 alembic 或 flask 迁移名称外键?

How to make alembic or flask migrate name foreign keys when autogenerating migrations?

我断断续续地为这个问题苦苦挣扎了很长一段时间,奇怪的是在 SO 上找不到直接的 question/answer 组合。相关问题here and here。我终于找到了解决方案,所以我会提出并回答我自己的问题。

在 Flask SQLAlchemy(和常规 SQLAlchemy)中,您可以拥有这样的列:

class Character(db.model):
  background_id = db.Column(db.Integer, db.ForeignKey('backgrounds.id'))

当您 运行 flask db migratealembic revision --autogenerate 时,这将导致如下所示的操作:

def upgrade():
  op.create_foreign_key(None, 'characters', 'backgrounds', ['background_id'], ['id'])

def downgrade():
  op.drop_constraint(None, 'characters', type_='foreignkey')

这里的None不好。事实上,如果你稍后尝试降级,这总是会失败,因为 drop_constraint 需要约束的名称。

您可以在每次生成迁移时更改它,如下所示:

def upgrade():
  op.create_foreign_key('fk_characters_backgrounds', 'characters', 'backgrounds', ['background_id'], ['id'])

def downgrade():
  op.drop_constraint('fk_characters_backgrounds', 'characters', type_='foreignkey')

哪个有效!

但是,如果您像我一样,不想每次使用外键自动生成修订版时都必须记住执行此操作。

所以问题是,我们怎样才能让它自动进行?

在“命名约定的重要性”部分末尾建议的最佳实践 here 中找到了这个问题的答案。解决方案是将 naming_convention 添加到 sqlalchemy 元数据,如下所示:

convention = {
  "ix": "ix_%(column_0_label)s",
  "uq": "uq_%(table_name)s_%(column_0_name)s",
  "ck": "ck_%(table_name)s_%(constraint_name)s",
  "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
  "pk": "pk_%(table_name)s"
}

metadata = MetaData(naming_convention=convention)

更具体地说,使用 Flask-SQLAlchemy,在初始化数据库时执行此操作:

from sqlalchemy import MetaData

convention = {
  "ix": "ix_%(column_0_label)s",
  "uq": "uq_%(table_name)s_%(column_0_name)s",
  "ck": "ck_%(table_name)s_%(constraint_name)s",
  "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
  "pk": "pk_%(table_name)s"
}

db = SQLAlchemy(metadata=MetaData(naming_convention=convention))

瞧!如果你 运行 autogenerate,你会得到这个:

def upgrade():
  op.create_foreign_key(op.f('fk_characters_background_id_backgrounds'), 'characters', 'backgrounds', ['background_id'], ['id'])

def downgrade():
  op.drop_constraint(op.f('fk_characters_background_id_backgrounds'), 'characters', type_='foreignkey')

感谢(意料之中的)Flask Migrate 的创建者 Miguel Grinberg,感谢他链接到 Alembic 文档中的正确页面,最终让我解决了这个问题! Someone had asked about this in an issue on Flask Migrate GitHub,他正确地指出这是一个 Alembic 问题,而不是 Flask Migrate 问题。