【问题标题】:Why Flask-migrate cannot upgrade when drop column为什么删除列时 Flask-migrate 无法升级
【发布时间】:2015-08-04 07:55:40
【问题描述】:

我正在使用 SqlAlchemy 和 Flask-migrate 进行数据库迁移。我已经成功init DB 和upgrade 一次,但是当我删除我的表列之一时,我设法migrate 但是upgrade 给了我以下错误:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) near "DROP": syntax error [SQL: u'ALTER TABLE posts DROP COLUMN tags']

我的models.py中有一部分

class Post(db.Model):
    __tabelname__ = 'posts'
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.UnicodeText)
    # tags = db.Column(db.Unicode(32))
    # I deleted this field, upgrade give me error

    ....

我再次运行python manage.py db upgrade,错误改变了!

(venv)ncp@ubuntu:~/manualscore$ python manage.py db upgrade
INFO  [alembic.migration] Context impl SQLiteImpl.
INFO  [alembic.migration] Will assume non-transactional DDL.
INFO  [alembic.migration] Running upgrade 555b78ffd5f -> 2e063b1b3164, add tag table
Traceback (most recent call last):
  File "manage.py", line 79, in <module>
    manager.run()
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/flask_script/__init__.py", line 405, in run
    result = self.handle(sys.argv[0], sys.argv[1:])
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/flask_script/__init__.py", line 384, in handle
    return handle(app, *positional_args, **kwargs)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/flask_script/commands.py", line 145, in handle
    return self.run(*args, **kwargs)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/flask_migrate/__init__.py", line 177, in upgrade
    command.upgrade(config, revision, sql=sql, tag=tag)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/alembic/command.py", line 165, in upgrade
    script.run_env()
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/alembic/script.py", line 390, in run_env
    util.load_python_file(self.dir, 'env.py')
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/alembic/util.py", line 243, in load_python_file
    module = load_module_py(module_id, path)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/alembic/compat.py", line 79, in load_module_py
    mod = imp.load_source(module_id, path, fp)
  File "migrations/env.py", line 72, in <module>
    run_migrations_online()
  File "migrations/env.py", line 65, in run_migrations_online
    context.run_migrations()
  File "<string>", line 7, in run_migrations
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/alembic/environment.py", line 738, in run_migrations
    self.get_context().run_migrations(**kw)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/alembic/migration.py", line 309, in run_migrations
    step.migration_fn(**kw)
  File "/home/ncp/manualscore/migrations/versions/2e063b1b3164_add_tag_table.py", line 24, in upgrade
    sa.PrimaryKeyConstraint('id')
  File "<string>", line 7, in create_table
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/alembic/operations.py", line 944, in create_table
    self.impl.create_table(table)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/alembic/ddl/impl.py", line 198, in create_table
    self._exec(schema.CreateTable(table))
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/alembic/ddl/impl.py", line 122, in _exec
    return conn.execute(construct, *multiparams, **params)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 914, in execute
    return meth(self, multiparams, params)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/sqlalchemy/sql/ddl.py", line 68, in _execute_on_connection
    return connection._execute_ddl(self, multiparams, params)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 968, in _execute_ddl
    compiled
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1146, in _execute_context
    context)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1339, in _handle_dbapi_exception
    exc_info
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 199, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1139, in _execute_context
    context)
  File "/home/ncp/manualscore/venv/local/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 442, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) table tags already exists [SQL: u'\nCREATE TABLE tags (\n\tid INTEGER NOT NULL, \n\tname VARCHAR(32), \n\tpost_id INTEGER, \n\tPRIMARY KEY (id), \n\tFOREIGN KEY(post_id) REFERENCES posts (id)\n)\n\n']

【问题讨论】:

标签: python sqlalchemy flask-migrate


【解决方案1】:

SQLite 不支持删除或更改列。但是,有一种方法可以通过在表级别进行更改来解决此问题:https://www.sqlite.org/lang_altertable.html

对于 Alembic/Flask-Migrate 用户来说更有用的是,Alembic 的 batch_alter_table 上下文管理器可以让您以自然的方式指定更改,并在后面进行一些“创建新表 - 复制数据 - 删除旧表 - 重命名新表”的舞蹈使用 SQLite 时的场景。见:http://alembic.zzzcomputing.com/en/latest/batch.html

所以迁移文件中的 upgrade() 函数应该包含如下内容:

with op.batch_alter_table('posts') as batch_op:
    batch_op.drop_column('tags')

恐怕我不知道为什么你第二次尝试升级时错误改变了。

正如 tkisme 指出的那样,您还可以在 env.py 中配置 EnvironmentContext.configure.render_as_batch 标志,以便自动生成的迁移脚本默认使用 batch_alter_table。见:http://alembic.zzzcomputing.com/en/latest/batch.html#batch-mode-with-autogenerate

【讨论】:

  • 我了解这个特定于 sqlite 的问题。但是为什么不自己检测到alembic并使用建议的batch_op呢?
  • 对于 alembic,当前链接是:alembic.zzzcomputing.com/en/latest/batch.html
  • @buhtz“为什么不自己检测到alembic并使用建议的batch_op”。可能是因为批处理模式仍被认为是测试版,或者因为这只适用于“在线”模式。
  • Here's a walkthrough video 对于那些(像我一样)努力将它自己整合在一起的人。
  • 谢谢,这解决了我烧瓶迁移的大问题!
【解决方案2】:

更改迁移/env.py,文档链接为http://alembic.readthedocs.org/en/latest/cookbook.html 在 context.configure 中添加render_as_batch=True

def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """

    # this callback is used to prevent an auto-migration from being generated
    # when there are no changes to the schema
    # reference: http://alembic.readthedocs.org/en/latest/cookbook.html
    def process_revision_directives(context, revision, directives):
        if getattr(config.cmd_opts, 'autogenerate', False):
            script = directives[0]
            if script.upgrade_ops.is_empty():
                directives[:] = []
                logger.info('No changes in schema detected.')

    engine = engine_from_config(config.get_section(config.config_ini_section),
                                prefix='sqlalchemy.',
                                poolclass=pool.NullPool)

    connection = engine.connect()
    context.configure(connection=connection,
                      target_metadata=target_metadata,
                      process_revision_directives=process_revision_directives,
                      render_as_batch=True,# this is new feature
                      **current_app.extensions['migrate'].configure_args)

    try:
        with context.begin_transaction():
            context.run_migrations()
    finally:
        connection.close()

【讨论】:

  • 不要只发布代码而没有解释。请详细说明此代码有效的原因。
【解决方案3】:

我不知道你解决了这个问题。 但我找到了一个非常简洁的解决方案: 您可以将其添加到 init.py 文件的底部。之后,删除并重建您的迁移:

with app.app_context():
    if db.engine.url.drivername == 'sqlite':
        migrate.init_app(app, db, render_as_batch=True)
    else:
        migrate.init_app(app, db)

希望能帮到你。

【讨论】:

  • 谢谢,这应该是公认的答案。这绝对是一个更优雅的解决方案。
  • 你可能还想要我写的其他内容github.com/miguelgrinberg/Flask-Migrate/issues/…
  • 添加此代码后,必须从头开始重新构建迁移文件。否则,您之前的迁移仍将使用 ALTER 命令。 Source
  • 为了澄清 Flask 用户,这应该放在 init.py 文件的底部。
【解决方案4】:

初始化迁移时,只需添加“render_as_batch=True”,如下所示:

migrate = Migrate(app, db, render_as_batch=True)  
manager = Manager(app)

这对我有用

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-24
    • 1970-01-01
    • 2018-09-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 2015-02-12
    • 1970-01-01
    相关资源
    最近更新 更多