更新模型后,Flask SQLAlchemy 升级失败.需要解释我的修复程序的工作原理



我正在关注一本书,试图创建一个Flask博客。我是Flask的新手。我更新了一个模型,所以代码看起来像这样:

class Entry(db.Model):
STATUS_PUBLIC = 0
STATUS_DRAFT = 1
STATUS_DELETED = 2
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
slug = db.Column(db.String(100), unique=True)
body = db.Column(db.Text)
status = db.Column(db.SmallInteger, default=STATUS_PUBLIC)
created_timestamp = db.Column(db.DateTime, default=datetime.datetime.now)
modified_timestamp = db.Column(
db.DateTime,
default=datetime.datetime.now,
onupdate=datetime.datetime.now)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))

添加最后一行author_id = db.Column(db.Integer, db.ForeignKey("user.id"))会导致数据库升级失败并显示错误:

File "manage.py", line 5, in <module>
manager.run()
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/flask_script/__init__.py", line 412, in run
result = self.handle(sys.argv[0], sys.argv[1:])
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/flask_script/__init__.py", line 383, in handle
res = handle(*args, **config)
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/flask_script/commands.py", line 216, in __call__
return self.run(*args, **kwargs)
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/flask_migrate/__init__.py", line 259, in upgrade
command.upgrade(config, revision, sql=sql, tag=tag)
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/alembic/command.py", line 254, in upgrade
script.run_env()
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/alembic/script/base.py", line 425, in run_env
util.load_python_file(self.dir, 'env.py')
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/alembic/util/pyfiles.py", line 93, in load_python_file
module = load_module_py(module_id, path)
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/alembic/util/compat.py", line 75, in load_module_py
mod = imp.load_source(module_id, path, fp)
File "migrations/env.py", line 87, in <module>
run_migrations_online()
File "migrations/env.py", line 80, in run_migrations_online
context.run_migrations()
File "<string>", line 8, in run_migrations
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/alembic/runtime/environment.py", line 836, in run_migrations
self.get_context().run_migrations(**kw)
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/alembic/runtime/migration.py", line 330, in run_migrations
step.migration_fn(**kw)
File "/home/devblog/projects/blog/app/migrations/versions/6b49bdaf06ce_.py", line 22, in upgrade
batch_op.create_foreign_key(None, 'user', ['author_id'], ['id'])
File "/usr/lib64/python2.7/contextlib.py", line 24, in __exit__
self.gen.next()
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/alembic/operations/base.py", line 299, in batch_alter_table
impl.flush()
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/alembic/operations/batch.py", line 80, in flush
fn(*arg, **kw)
File "/home/devblog/projects/blog/venv/lib/python2.7/site-packages/alembic/operations/batch.py", line 337, in add_constraint
raise ValueError("Constraint must have a name")
ValueError: Constraint must have a name

问题似乎出在迁移脚本的升级功能上:

def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('entry', schema=None) as batch_op:
#batch_op.create_foreign_key(None, 'user', ['author_id'], ['id'])
batch_op.create_foreign_key("author_id", 'user', ['author_id'], ['id'])

我通过注释掉原始行、删除None并使用'author_id'来"修复"此问题。

我 http://alembic.zzzcomputing.com/en/latest/ops.html 使用了此处的文档来提出修复程序。相关部分:

create_foreign_key(constraint_name、source_table、referent_table、 local_cols, remote_cols, onupdate=none, ondelete=none, 可延迟 = 无,最初=无,匹配=无,source_schema=无, referent_schema=无,**dialect_kw(

这将在内部生成一个 Table 对象,其中包含必要的 列,然后生成一个新的外键约束对象,它 然后与表关联。与 关联的任何事件侦听器 此操作将正常触发。构造为 最终用于生成 ALTER 语句。

参数:名称 – 外键约束的名称。名称是 必需的,以便可以发出 ALTER 语句。对于以下设置 使用自动命名方案,如配置中所述的方案 约束命名约定,此处的名称可以是 None,作为事件 侦听器将在约束对象 与表关联。source_table – 源的字符串名称 桌子。referent_table – 目标表的字符串名称。 local_cols – 源表中字符串列名的列表。 remote_cols – 远程表中字符串列名的列表。 onupdate – 可选字符串。如果设置,则在以下情况下发出更新时发出 为此约束发出 DDL。典型值包括级联、 删除和限制。ondelete – 可选字符串。如果设置,则发出 ON 为此约束发出 DDL 时删除。典型值 包括级联、删除和限制。可延迟 – 可选布尔值。如果 为此发布 DDL 时设置、发出可延迟或不可延迟 约束。source_schema – 源表的可选架构名称。 referent_schema¶ – 目标表的可选架构名称。

我想我添加了一个约束名称而不是None,这已经"解决"了这个问题。这是对的吗?

此外,看起来函数工作需要 5 个参数。仅使用 4 个如何工作?

任何建议都非常感谢。

Mark Steward 的解决方案非常适合我添加简单外键列的情况。

无论您在哪里声明/定义"db",请执行以下操作 -

from sqlalchemy import MetaData
naming_convention = {
"ix": 'ix_%(column_0_label)s',
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(column_0_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=naming_convention))

然后,在初始化迁移应用(烧瓶迁移(的位置,传递render_as_batch=True-

migrate.init_app(app, db, render_as_batch=True)

最新更新