如何使用Flask-Migrate做数据库迁移?
How to use Flask-Migrate to do database migration?
我在使用 Flask-Migrate 到 upgrade/downgrade 数据库时遇到问题。有两个 table,User
和 Post
,由以下 class 定义:
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
并且我在这些 table 中添加了一些条目。
现在是问题。 :)
为了好玩,我通过 dummy = db.Column(db.String(20))
在 User
table 中添加了一列。修改 table 架构后,我 运行 以下命令:
flask db migrate
----成功
flask db upgrade
----成功
然后我想回去:
- 要将数据库恢复到以前的状态,我 运行
flask db downgrade
---- 失败并显示错误:sqlite3.OperationalError: near "DROP": syntax error
- 我试图通过从
User
class中删除dummy = db.Column(db.String(20))
语句来修复错误,然后再次运行flask db downgrade
----再次失败同样的错误。
- 然后我想也许 Flask-Migrate 需要知道删除,所以我 运行
flask db migrate
---- 成功
- 尝试
flask db upgrade
---- 再次失败,出现同样的错误。
那么 flask-migrate 是如何工作的呢?具体来说,如何将数据库恢复到初始状态(没有 dummy
属性)?
谢谢!
Flask migrate 在您 运行 flask db migrate
命令时生成迁移脚本。您可以查看这些迁移文件中的命令,看看它们在做什么,并确保它们在做正确的事情。事实上,它建议您在 运行 启用它们之前检查自动生成的迁移脚本。
如果您更改某些内容并且不重新 运行 migrate
命令,那么升级和降级都不会做任何不同的事情,因为它们只是 运行 migrate 中相应的迁移脚本文件夹。
如果数据库不重要,最简单的做法是删除数据库和迁移脚本,然后再次 运行 flask db init
并重新开始了解有关迁移脚本的新知识,然后查看如果能拿到升级降级的手
SQLite 本身不支持删除或更改列(显然我猜你使用的是 SQLite)。这意味着 SQLite 不支持 ALTER 语句,但关系模式迁移依赖于此语句。
要解决此问题,您必须制作一系列对应于新结构的 SQLite table 副本,将数据从现有 table 转移到新结构,然后删除旧的 table.
幸运的是,对于 Alembic / Flask-migrate,有一个 上下文管理器 ( batch_alter_table
) 可以让您轻松管理所有这些更改。
对于您的情况,解决方案是打开您的迁移脚本并在 downgrade()
方法级别,将那里的指令(可能 op.drop_column('roles', 'dummy')
)替换为:
with op.batch_alter_table('roles') as batch_op:
batch_op.drop_column('dummy')
有关详细信息,请参阅 this link。
一些小细节:
使用迁移工具时,请始终牢记自动迁移并不总是准确的,并且可能会遗漏一些细节。应始终审查自动生成的迁移脚本。
以防万一,降级数据库时,确保删除迁移脚本,然后生成一个新脚本来替换它。
我在使用 Flask-Migrate 到 upgrade/downgrade 数据库时遇到问题。有两个 table,User
和 Post
,由以下 class 定义:
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
并且我在这些 table 中添加了一些条目。
现在是问题。 :)
为了好玩,我通过 dummy = db.Column(db.String(20))
在 User
table 中添加了一列。修改 table 架构后,我 运行 以下命令:
flask db migrate
----成功flask db upgrade
----成功
然后我想回去:
- 要将数据库恢复到以前的状态,我 运行
flask db downgrade
---- 失败并显示错误:sqlite3.OperationalError: near "DROP": syntax error
- 我试图通过从
User
class中删除dummy = db.Column(db.String(20))
语句来修复错误,然后再次运行flask db downgrade
----再次失败同样的错误。 - 然后我想也许 Flask-Migrate 需要知道删除,所以我 运行
flask db migrate
---- 成功 - 尝试
flask db upgrade
---- 再次失败,出现同样的错误。
那么 flask-migrate 是如何工作的呢?具体来说,如何将数据库恢复到初始状态(没有 dummy
属性)?
谢谢!
Flask migrate 在您 运行 flask db migrate
命令时生成迁移脚本。您可以查看这些迁移文件中的命令,看看它们在做什么,并确保它们在做正确的事情。事实上,它建议您在 运行 启用它们之前检查自动生成的迁移脚本。
如果您更改某些内容并且不重新 运行 migrate
命令,那么升级和降级都不会做任何不同的事情,因为它们只是 运行 migrate 中相应的迁移脚本文件夹。
如果数据库不重要,最简单的做法是删除数据库和迁移脚本,然后再次 运行 flask db init
并重新开始了解有关迁移脚本的新知识,然后查看如果能拿到升级降级的手
SQLite 本身不支持删除或更改列(显然我猜你使用的是 SQLite)。这意味着 SQLite 不支持 ALTER 语句,但关系模式迁移依赖于此语句。
要解决此问题,您必须制作一系列对应于新结构的 SQLite table 副本,将数据从现有 table 转移到新结构,然后删除旧的 table.
幸运的是,对于 Alembic / Flask-migrate,有一个 上下文管理器 ( batch_alter_table
) 可以让您轻松管理所有这些更改。
对于您的情况,解决方案是打开您的迁移脚本并在 downgrade()
方法级别,将那里的指令(可能 op.drop_column('roles', 'dummy')
)替换为:
with op.batch_alter_table('roles') as batch_op:
batch_op.drop_column('dummy')
有关详细信息,请参阅 this link。
一些小细节:
使用迁移工具时,请始终牢记自动迁移并不总是准确的,并且可能会遗漏一些细节。应始终审查自动生成的迁移脚本。
以防万一,降级数据库时,确保删除迁移脚本,然后生成一个新脚本来替换它。