flask-migrate/alembic fails with `ValueError: not enough values to unpack (expected 2, got 1)` when running db migrate

flask-migrate/alembic fails with `ValueError: not enough values to unpack (expected 2, got 1)` when running db migrate

我创建了一个 Flask 应用程序,目前有大约 30 个模型。 到目前为止,当我在本地测试所有内容时,我有一个本地 Postgresql docker 容器,我将在该容器上使用 SQLAlchemy().create_all() 创建所有 table。到目前为止效果很好。但是,我想,为了让这个应用程序更适合生产,我应该开始使用迁移,而不是每次进行架构更改时 运行ning create_all()。因此,我通过遵循 Flask-migrate docs、运行 python manage.py db init 成功实现了迁移命令,但是当我 运行 python manage.py db migrate 时,我得到了一个很长的堆栈跟踪,以ValueError: not enough values to unpack (expected 2, got 1)。 我发现 this 先前提出的问题与我的错误相匹配,我尝试按照解决方法进行操作(我只有一个 table 和 __table_args__ 指定两个检查约束,如下所示):

    __table_args__ = (
        db.CheckConstraint('minutes >= 0', name='check_minutes_positive'),
        db.CheckConstraint('seconds between 0 and 59', name='check_seconds_valid')
    )

但即使我将其注释掉,我仍然遇到同样的错误。 我什至注释掉了每个模型中除主键之外的每个字段,但仍然出现相同的错误。

我已经尝试将 alembic.ini 文件中每个记录器的日志级别提高到 DEBUG,但这无助于指出此错误的根本原因。堆栈跟踪中的最后一次调用指向 api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 808, in _fk_colspec,但这并没有告诉我太多信息,因为我无法弄清楚我这边可能缺少什么导致了 ValueError。 如果能帮助我理解如何找到问题,我将不胜感激。

这是 migrate 命令的完整输出(我只删除了显示 table 名称和创建的索引的大部分初始 INFO 行,因为我认为它们并不真正相关。仅留下第一个和最后一个):

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'tAbstractQuestions'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_tAbstractQuestions_questionTypeId' on '['questionTypeId']'
INFO  [alembic.autogenerate.compare] Detected added table 'tQuestionsOptions'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_tQuestionsOptions_questionId' on '['questionId']'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_tQuestionsOptions_questionOptionId' on '['questionOptionId']'
Traceback (most recent call last):
  File "manage.py", line 70, in <module>
    manager.run()
  File "/api/.venv/lib/python3.8/site-packages/flask_script/__init__.py", line 417, in run
    result = self.handle(argv[0], argv[1:])
  File "/api/.venv/lib/python3.8/site-packages/flask_script/__init__.py", line 386, in handle
    res = handle(*args, **config)
  File "/api/.venv/lib/python3.8/site-packages/flask_script/commands.py", line 216, in __call__
    return self.run(*args, **kwargs)
  File "/api/.venv/lib/python3.8/site-packages/flask_migrate/__init__.py", line 96, in wrapped
    f(*args, **kwargs)
  File "/api/.venv/lib/python3.8/site-packages/flask_migrate/__init__.py", line 210, in migrate
    command.revision(config, message, autogenerate=True, sql=sql,
  File "/api/.venv/lib/python3.8/site-packages/alembic/command.py", line 221, in revision
    scripts = [script for script in revision_context.generate_scripts()]
  File "/api/.venv/lib/python3.8/site-packages/alembic/command.py", line 221, in <listcomp>
    scripts = [script for script in revision_context.generate_scripts()]
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/api.py", line 513, in generate_scripts
    yield self._to_script(generated_revision)
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/api.py", line 425, in _to_script
    render._render_python_into_templatevars(
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 49, in _render_python_into_templatevars
    _render_cmd_body(upgrade_ops, autogen_context)
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 71, in _render_cmd_body
    lines = render_op(autogen_context, op)
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 87, in render_op
    lines = util.to_list(renderer(autogen_context, op))
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 179, in _add_table
    for rcons in [
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 180, in <listcomp>
    _render_constraint(cons, autogen_context)
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 775, in _render_constraint
    return renderer(constraint, autogen_context)
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 871, in _render_foreign_key
    "refcols": ", ".join(
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 872, in <genexpr>
    repr(_fk_colspec(f, apply_metadata_schema))
  File "/api/.venv/lib/python3.8/site-packages/alembic/autogenerate/render.py", line 808, in _fk_colspec
    tname, colname = tokens[-2:]
ValueError: not enough values to unpack (expected 2, got 1)

谢谢!

我在 Alembic 的 GitHub 回购上发布了同样的问题,并在那里指出了正确的方向。 Link to the issue.

同时粘贴以下解决方案:

Found it! I decided I'm diving deeper. So what I did is to comment-out every table that had a foreign key in it and ran migrate - worked. Then I started adding one table at a time from the ones that did have a foreign key and voila! I found that one of the tables had a db.foreignKey('tableName') (without specifying the actual column on which the constraint should apply ). So no bug here, but I guess a more descriptive error would've saved me a good bit of time spent over this, lol. To verify- after fixing the misconfiguration, I cleaned up the migrations folder and restarted my PostgreSQL container (which started a brand new db from scratch) and ran the migrate command to create a single revision which includes all models at once- and it works!