为什么Flask-migrate在drop column时无法升级
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么Flask-migrate在drop column时无法升级相关的知识,希望对你有一定的参考价值。
我正在使用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'
CREATE TABLE tags (
id INTEGER NOT NULL,
name VARCHAR(32),
post_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(post_id) REFERENCES posts (id)
)
']
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指出的那样,您还可以在EnvironmentContext.configure.render_as_batch
中配置env.py
标志,以便自动生成的迁移脚本默认使用batch_alter_table
。见:http://alembic.zzzcomputing.com/en/latest/batch.html#batch-mode-with-autogenerate
在context.configure中更改迁移/ env.py添加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()
我不知道你解决了这个问题。但我找到了一个非常简洁的解决方案:你可以这样做:
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)
希望能帮到你。
以上是关于为什么Flask-migrate在drop column时无法升级的主要内容,如果未能解决你的问题,请参考以下文章
col.drop_duplicates() 有更快的替代方法吗?