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

时间:2017-08-05 23:46:24

标签: python sqlite

我正在跟着一本书来尝试创建一个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,   deferrable = None,initial = None,match = None,source_schema = None,   referent_schema = None,** dialect_kw)

     

这在内部生成一个包含必要的Table对象   列,然后生成一个新的ForeignKeyConstraint对象   然后与表联系。与之关联的任何事件侦听器   这个动作将正常发射。 AddConstraint构造是   最终用于生成ALTER语句。

     

参数:name - 外键约束的名称。名字是   必要的,以便可以发出ALTER语句。对于那些设置   使用自动命名方案,如配置中所述   约束命名约定,此处的名称可以是None,作为事件   侦听器将该名称应用于约束对象   与表相关联。 source_table - 源的字符串名称   表。 referent_table - 目标表的字符串名称。   local_cols - 源表中的字符串列名称列表。   remote_cols - 远程表中的字符串列名列表。   onupdate - 可选字符串。如果设置,则在发出ON UPDATE时   为此约束发出DDL。典型值包括CASCADE,   删除和限制。 ondelete - 可选字符串。如果设置,则发出ON   为此约束发出DDL时删除。典型值   包括CASCADE,DELETE和RESTRICT。可延迟的 - 可选的布尔。如果   设置,在为此发出DDL时发出DEFERRABLE或NOT DEFERRABLE   约束。 source_schema - 源表的可选模式名称。   referent_schema¶ - 目标表的可选模式名称。

认为我添加了一个约束名称,而不是None,它已经“修复”了这个问题。这是对的吗?

此外,看起来该函数需要5个参数才能工作。它如何只用4?

感谢任何建议。

1 个答案:

答案 0 :(得分:4)

Mark Steward's solution对我来说非常适合添加简单的外键列。

无论您在何处声明/定义&#34; db&#34;,请执行以下操作 -

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))

然后,在初始化迁移应用程序(flask-migrate)的地方,传递render_as_batch=True -

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