我断断续续地解决这个问题已经有一段时间了,奇怪的是找不到一个简单的问题/答案组合。相关问题这里和这里。我终于找到了解决方案,所以我会问并回答我自己的问题。
在 Flask SQLAlchemy(和常规 SQLAlchemy)中,您可以有一个像这样的列:
class Character(db.model):
background_id = db.Column(db.Integer, db.ForeignKey('backgrounds.id'))
当您运行
flask db migrate
或 alembic 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')
这有效!
但是,如果您像我一样,您不想每次使用外键自动生成修订版时都必须记住执行此操作。
所以问题是,我们怎样才能让它自动化?
在“命名约定的重要性”部分末尾的此处建议的最佳实践中可以找到此问题的答案。解决方案是将
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 文档中的正确页面,最终让我解决了这个问题! 有人在 Flask Migrate GitHub 上的一个问题中询问过这个问题,Miguel 正确地指出这是一个 Alembic 问题,而不是 Flask Migrate 问题。