Flask 中的数据库迁移

2021-02-26 15:42:43 浏览数 (1)

在我之前使用 Flask 实现简单接口时,为了方便,我每次都会将数据表删除掉,然后重新创建表和添加数据。因为测试数据只有几条,所以可以使用删表重建的方式,但在实际的项目中,是不可能使用这种方式的,删表意味着删数据。

在开发过程中,有时候需要修改数据库模型,比如新功能需要增加一个字段,在 Flask 代码中修改模型类后,要将新增的字段同步到数据库中。这时候是不能删表重建的。

在 Flask 中,可以使用数据库迁移来解决这个问题,数据库迁移可以追踪数据模型类的变化,然后把变动应用到数据库中,不会删表造成数据丢失。

一、安装 Flask-Migrate 和 Flask-Script

在 Flask 中使用 Flask-Migrate 扩展,来实现数据迁移。

Flask-Migrate 的文档路径:https://flask-migrate.readthedocs.io/en/latest/ ,内容不多,可以看看。

先安装 Flask-Migrate 。

代码语言:javascript复制
pip install Flask-Migrate

执行安装命令,会自动下载和安装 Flask-Migrate 模块及相关的依赖库。

其中一个非常重要的依赖库是 Alembic ,数据库迁移时自动生成迁移文件和迁移脚本都是 Alembic 完成的,也是因为 Alembic 的机制,数据库迁移操作只能在 Linux 系统中使用,不能在 Windows 中使用。

具体可以仔细研究一下 Alembic ,文档路径:https://alembic.sqlalchemy.org/en/latest/tutorial.html 。

另外,需要用到 Flask-Script 模块,使用 Flask-Script 来管理 Flask 应用程序 app ,Flask 程序中的操作可以通过命令来完成。

Flask-Migrate 提供了一个 MigrateCommand 类,将这个类添加到 Flask-Script 的 Manager 对象中,可以更方便地使用命令来进行数据库迁移,Flask-Migrate 文档中也是推荐这种方式的。

所以,也要安装 Flask-Script 。

代码语言:javascript复制
pip install Flask-Script

二、准备数据库迁移的模型类

在项目文件夹下创建一个 flask_migrate_db.py 文件,注意文件名不要叫 flask_migrate.py ,否则会与 Flask-Migrate 中的文件名冲突,导包时就会报错。

先在 flask_migrate_db.py 中编写如下代码,做好数据库迁移的准备。

代码语言:javascript复制
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://admin:Mysql!123@127.0.0.1:3306/MyDB_two'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_ECHO'] = True
app.config['SECRET_KEY'] = 'NFAIOSDFHASOGHAOSPIGAOWE'
db = SQLAlchemy(app)

migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)


class Computer(db.Model):
    __tablename__ = 'Computer_tb'
    cid = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))
    # price = db.Column(db.Integer)
    # color = db.Column(db.String(64))
    user_id = db.Column(db.Integer, db.ForeignKey('User_tb.uid'))

    def __repr__(self):
        return 'Computer_name: {}'.format(self.name)


class User(db.Model):
    __tablename__ = 'User_tb'
    uid = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    phones = db.relationship('Computer', backref='user', lazy='dynamic')

    def __repr__(self):
        return 'User_name: {}'.format(self.name)


@app.route('/')
def index():
    return 'Hello Migrate!'


if __name__ == '__main__':
    # user_one = User(name='You')
    # db.session.add(user_one)
    # computer_one = Computer(name='Dell', user_id=1)
    # db.session.add(computer_one)
    # db.session.commit()

    manager.run()

代码中连接了 mysql 数据库 MyDB_two,这个数据库需要提前创建好。将 Flask 应用对象 app 和数据库连接对象 db 传入 Migrate ,创建迁移对象 migrate 。将 MigrateCommand 添加到 Flask-Script 的 Manager 中,最后使用 Manager 管理和运行 app 。

代码中定义了两个数据库模型类 Computer 和 User ,执行数据库迁移后会在 MyDB_two 中创建两张表 Computer_tb 和 User_tb 。

三、执行数据库迁移

1. 初始化迁移仓库

代码语言:javascript复制
python flask_migrate_db.py db init

在项目目录下执行 init 初始化命令,会自动在项目目录下创建一个 migrations 目录。这个目录是 Alembic 模块自动创建的,默认名字叫 migrations ,可以在创建 migrate = Migrate(app, db) 对象时传入 directory='filename' 参数自定义名字。

migrations 里面有一个 versions 文件夹,这个文件夹用于存放迁移脚本,执行迁移命令后会自动生成迁移脚本保存在里面。

env.py 是迁移环境的相关信息。

数据库迁移时,初始化命令只需要执行一次,如果在一开始发现执行有问题,需要重新初始化,要先删除 migrations 目录才行。如果已经执行迁移命令,需要重新初始化,要先删除 migrations 目录和到数据库中删除 alembic_version 表。

2. 创建迁移脚本

代码语言:javascript复制
python flask_migrate_db.py db migrate -m "create table"

继续执行 migrate 命令生成迁移脚本,通过 -m 参数添加迁移信息,这类似于 git 提交代码时添加提交信息。

执行命令后,会在 versions 目录下生成一个迁移脚本,迁移脚本的名字是版本 id 和迁移信息拼接的结果,打开迁移脚本,脚本里定义了一个 upgrade() 函数,函数里的代码就是创建数据表的代码,感兴趣可以打开看看。

每次数据模型类有变化,需要迁移数据库时,都需要执行创建迁移脚本的命令,生成新版本的迁移脚本。

3. 执行数据库迁移

代码语言:javascript复制
python flask_migrate_db.py db upgrade

生成迁移脚本后,数据库还没有变化,迁移结果还没有生效,需要继续执行 upgrade 命令,使迁移结果应用到数据库中。执行 upgrade 命令相当于执行迁移脚本中的 upgrade() 函数,执行里面创建表的代码。

执行 upgrade 命令后,会在数据库中创建一张 alembic_version 表,这张表不是代码中定义的,是 Alembic 自动创建的(看名字就知道了),里面保存的是当前数据库的版本 id ,alembic_version 表不能删除,删除后就不能继续执行数据库迁移操作了,除非重新初始化。

同时,执行 upgrade 命令后,会根据代码中定义的模型类创建对应的表,表的字段与模型类中定义的一致。

如果数据库中有其他表(没有对应模型类的表),会被删除。这点需要特别注意,数据库迁移时最好使用一个新的数据库(不要与其他项目用同一个数据库),避免造成数据丢失。

每次生成迁移脚本后,都需要执行 upgrade 命令,迁移结果才会生效。

4. 添加数据和添加字段

现在已经执行了第一次数据库迁移,数据库中创建了对应的表,但是表都是空的,没有数据。

添加下面的代码,然后 python flask_migrate_db.py 运行代码,在两张表里面各添加一条数据。

代码语言:javascript复制
    user_one = User(name='You')
    db.session.add(user_one)
    computer_one = Computer(name='Dell', user_id=1)
    db.session.add(computer_one)
    db.session.commit()

添加后,到数据库中查询,确认数据添加成功,数据库已经可以正常使用了。

现在,假设需要修改数据库模型类,如需要在模型类 Computer 中增加一个 price 字段。先注释掉添加数据的代码(有唯一字段不能重复运行和添加),然后在 Computer 类中添加一个字段。

代码语言:javascript复制
    price = db.Column(db.Integer)

5. 生成新版本数据库迁移脚本

代码语言:javascript复制
python flask_migrate_db.py db migrate -m "add price to computer"

模型类的代码修改后,数据表并没有变化,需要重新生成迁移脚本和执行数据库迁移。

执行命令后,会生成一个新的迁移脚本,打开新的迁移脚本,脚本里面的代码就是执行数据库新增字段的代码。

前面提到,在 Linux 系统中可以顺利执行数据库迁移,在 Windows 中会失败,是因为 Alembic 生成迁移脚本的机制,现在就简单解释一下原因。

执行上面相同的操作后,打开迁移脚本,Linux 系统和 Windows 系统中生成的迁移脚本代码是不同的。Linux 中的脚本代码是直接添加字段,Windows 中的脚本代码包含了删除关系字段、删除表和重新创建表的代码,而且顺序是乱的(创建在前删除在后,删除表的顺序也不对),所以在 Windows 中执行迁移时会失败。

如果要在 Windows 中成功执行数据库迁移,就不能直接使用 Alembic 生成的迁移脚本,需要自己修改迁移脚本。可以调整迁移脚本中代码的顺序,先删除关系字段,关系表,然后删除其他表,最后创建新表,这样执行迁移后,相当于删表重建,表结构修改成功了,但是数据丢失了。更好的方法是将代码改成增加字段的代码(与Linux中的一样),再执行迁移,就能完成修改表结构并保留数据。

所以,要在 Windows 中执行数据库迁移,要知道怎么改迁移脚本(与模型类变化一致,改时要细心)。

6. 执行迁移

代码语言:javascript复制
python flask_migrate_db.py db upgrade

最后执行 upgrade 命令使迁移脚本生效。

执行之后,成功在 Computer_tb 表中增加了字段 price 。

再查询一下数据,看数据是否丢失。

可以看到,数据都保留着,之前的数据没有新字段的值,默认为空 NULL 。

7. 其他操作

在所有迁移脚本中,除了 upgrade() 函数外,还有一个 downgrade() 函数,这两个函数里面的代码是相反的,downgrade() 函数是用于回退数据库迁移的。

upgrade() 函数把迁移中的改动应用到数据库中,downgrade() 函数则将改动删除。

代码语言:javascript复制
python flask_migrate_db.py db downgrade

执行 downgrade 命令如果不指定版本 id ,默认是回退到上一个版本,即版本 -1 (减一),也可以指定版本回退。

对数据库迁移后,可以使用 history 命令找到历史的版本号和变更过程。

代码语言:javascript复制
python flask_migrate_db.py db history

此外,数据库迁移时还可以指定参数来完成更多的功能,如初始化时指定 --multi 参数可以实现多数据库迁移(同时使用多种数据库,如 mysql postgresql ),生成迁移脚本和执行迁移时使用 --sql 参数可以查看数据库迁移命令对应的原生 SQL 语句。具体使用方法可以去查看 Flask-Migrate 的文档。

0 人点赞