主题
数据库迁移
Flask-Migrate
是一个用于管理数据库迁移的 Flask 扩展,它基于 Alembic 构建。
Alembic 是一个轻量级的数据库迁移工具,专门为 SQLAlchemy 设计。
通过 Flask-Migrate
,你可以轻松地管理和应用数据库模式的变化,而不需要手动编写 SQL 脚本或直接修改数据库结构。
主要作用
版本控制:
Flask-Migrate
允许你对数据库模式进行版本控制。每次你修改了数据库模型(如添加、删除或更改表字段),都可以创建一个新的迁移脚本来记录这些变化。
自动迁移:
- 它可以自动生成迁移脚本,基于模型定义的变化。这大大减少了手动编写复杂 SQL 语句的工作量,并降低了出错的可能性。
逐步更新:
- 使用
Flask-Migrate
可以确保数据库模式按照预定的顺序逐步更新,从而避免一次性大规模变更带来的风险。这对于生产环境尤其重要,因为它允许你在不影响服务的情况下安全地部署新特性或修复问题。
- 使用
回滚支持:
- 如果在某个迁移之后发现错误或者需要恢复到之前的版本,
Flask-Migrate
提供了方便的回滚功能,使你可以撤销最近的一次或多次迁移。
- 如果在某个迁移之后发现错误或者需要恢复到之前的版本,
多数据库支持:
- 支持多种数据库后端,包括 SQLite, PostgreSQL, MySQL 等等。
与 Flask-SQLAlchemy 集成良好:
Flask-Migrate
和Flask-SQLAlchemy
配合得非常好,能够很好地适应 Flask 应用程序中常见的 ORM 模式。
使用场景
- 开发阶段:在开发过程中频繁地调整数据库模型时,
Flask-Migrate
可以帮助开发者快速迭代数据库设计。 - 测试环境:在测试环境中,你可以使用迁移来同步不同开发人员之间的数据库状态,保证所有人的工作环境一致。
- 生产环境:当应用程序上线后,随着业务需求的变化,数据库模式也需要相应地调整。此时,
Flask-Migrate
可以确保这些变化被有序且安全地应用到生产数据库上。
安装依赖
shell
pip install flask-migrate
初始化迁移仓库
bash
export FLASK_APP=app.py
flask db init
执行后输出
shell
Creating directory '/case2_migrate_db/migrations' ... done
Creating directory '/case2_migrate_db/migrations/versions' ... done
Generating case2_migrate_db/migrations/script.py.mako ... done
Generating /case2_migrate_db/migrations/env.py ... done
Generating /case2_migrate_db/migrations/README ... done
Generating /case2_migrate_db/migrations/alembic.ini ... done
Please edit configuration/connection/logging settings in '/case2_migrate_db/migrations/alembic.ini' before proceeding.
创建新的迁移脚本
当你更改了数据库模型后,运行此命令来自动生成一个新的迁移脚本,例如migrations/versions/fadf2c7dd053_initial_migration.py
bash
flask db migrate -m "Initial migration"
应用迁移到数据库
这将执行最新的迁移脚本,更新数据库模式。
bash
flask db upgrade
case2_migrate_db % flask db upgrade
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> fadf2c7dd053, Initial migration
版本升级 fadf2c7dd053-> a78c7f4db3fd
新增字段: full_name和age
python
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
# 关系
roles = db.relationship('Role', secondary=user_roles, backref=db.backref('users', lazy='dynamic'))
posts = db.relationship('Post', backref='author', lazy=True)
python
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
full_name = db.Column(db.String(120))
age = db.Column(db.Integer)
# 关系
roles = db.relationship('Role', secondary=user_roles, backref=db.backref('users', lazy='dynamic'))
posts = db.relationship('Post', backref='author', lazy=True)
shell
case2_migrate_db % flask db migrate -m "Add full_name and age fields"
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added column 'users.full_name'
INFO [alembic.autogenerate.compare] Detected added column 'users.age'
Generating /case2_migrate_db/migrations/versions/a78c7f4db3fd_add_full_name_and_age_fields.py ... done
shell
case2_migrate_db % flask db upgrade
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade fadf2c7dd053 -> a78c7f4db3fd, Add full_name and age fields
回滚迁移 a78c7f4db3fd-> fadf2c7dd053
如果需要撤销最近一次或指定次数的迁移,可以使用以下命令。
bash
flask db downgrade
case2_migrate_db % flask db downgrade
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running downgrade a78c7f4db3fd -> fadf2c7dd053, Add full_name and age fields
查看代码
https://gitee.com/PatrickW/flask-web/blob/master/src/case2_migrate_db