Flask下数据库迁移
背景
在很多时候,在我们设计好模型之后,又需要进行一定的改变和更新数据操作;最直接的方式就是删除原来的旧表,但我们之前的数据也会删除;究其原因是我们不能精确记住每个修改和对应DDL,这时候就需要数据库迁移框架了。
对于Django ,框架内部就集成了数据库迁移模块,我们可以很方便使用
- 回顾Django迁移命令:
基本语法:
python manage.py xx [app_name]
xx如下:
migrate,负责应用和撤销迁移。
makemigrations,基于模型的修改创建迁移。
sqlmigrate,展示迁移使用的 SQL 语句。
showmigrations,列出项目的迁移和迁移的状态。
- Django迁移过程分为3步走:
1:修改models数据模型
2:生成迁移文件(在migrations/文件夹下)python manage.py makemigrations
3:向数据库执行迁移操作:python manage.py migrate
- 总结,迁移数据库的主要思路:
1:修改数据模型
2:生成新迁移文件
3:执行迁移
那么在Flask + SQLAlchemy下,如何选择数据库管理迁移工具?
Alembic介绍
Alembic
是Sqlalchemy
的作者就已经实现的一个数据库版本化管理工具:
Alembic来帮助我们实现数据库的迁移,数据库迁移工具可以在不破坏数据的情况下更新数据库表的结构。蒸馏器(Alembic)是炼金术士最重要的工具,要学习SQL炼金术(SQLAlchemy),当然要掌握蒸馏器的使用。
快速上手
# 安装alembic
pip install alembic
# 初次使用时,需要初始化,创建迁移环境和 alembic.ini 文件、env.py文件
alembic init migrations
# 修改alembic.ini配置,改变 sqlalchemy.url 值,配置数据库连接。
sqlalchemy.url = mysql+pymysql://root:mysql@localhost/ocean_209_old
# 生成数据库迁移文件。
# --autogrenerate 选项参数来自动生成迁移脚本(这里演示使用手动:仅生成空模板)
alembic revision -m "first comment"
# 同步数据库
alembic upgrade head
Alembic 实践
1、创建 Alembic 迁移环境
在使用 Alembic 之前需要先建立一个 Alembic 脚本环境,通过在工程目录下输入
# 切换到项目根目录下
alembic init alembic
# init之后的alembic指的是保存迁移文件的目录名称。
# 执行之后会在项目根目录下生成 alembic.ini文件 和制定名称的迁移文档保存目录()。
命令可以快速在应用程序中建立 Alembic 脚本环境,命令行看到以下输出:
$ alembic init alembic
Creating directory C:\Projects\ocean\alembic ... done
Creating directory C:\Projects\ocean\alembic\versions ... done
Generating C:\Projects\ocean\alembic.ini ... done
Generating C:\Projects\ocean\alembic\env.py ... done
Generating C:\Projects\ocean\alembic\README ... done
Generating C:\Projects\ocean\alembic\script.py.mako ... done
Please edit configuration/connection/logging settings in 'C:\\Projects\\ocean\\alembic.ini' before proceeding.
alembic生成的目录和文件如下:
alembic
├── README
├── env.py
├── script.py.mako
└── versions/
alembic.ini
alembic 目录:迁移脚本的根目录,放置在应用程序的根目录下,可以设置为任意名称。在多数据库应用程序可以为每个数据库单独设置一个 Alembic 脚本目录。
- versions 目录:用于存放各个版本的迁移脚本。初始情况下为空目录,通过
revision
命令可以生成新的迁移脚本。 - README 文件:说明文件,初始化完成时没有什么意义。
- env.py 文件:用来配置和实例化 SQLAlchemy 引擎,提供连接引擎并进行事务,保证当 Alembic 执行命令时被合理的调用。
- script.py.mako 文件:模板文件,定义生成迁移文件的基本模板。(我们可以在这里添加一些代码)
alembic.ini文件:在开始任何的操作之前需要先配置该文件中的 sqlalchemy.url
指向你自己的数据库地址。
2、修改配置
修改 alembic.ini
配置所使用的数据库连接,即需要迁移的数据库地址
# mysql
sqlalchemy.url = mysql+pymysql://root:mysql@localhost/ocean_209_old
# postgres
sqlalchemy.url = postgresql+psycopg2://root:123456@127.0.0.1/table_name
修改 env.py
为了让alembic追踪模型信息,与ini相比较,需要将
target_metadata
赋值为要追踪的 metadata
根据需要,选择一个来源,将target_metadata = None
指向其中一个metadata
"""配置target_metadata"""
# 来源一:Base
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
from ocean.model._base import Base # 不同代码结构不同
metadata = Base.metadata
# # 来源二:已存在表
from sqlalchemy import create_engine
from sqlalchemy.schema import MetaData
engine = create_engine('mysql+pymysql://root:mysql@localhost/ocean_253_test_alembic')
metadata = MetaData(engine)
# # only参数可传入指定的数据库表名列表,表示只对这些表生成迁移脚本,否则就对DB中的全表生成
# metadata.reflect(only=["users"])
metadata.reflect() # 引入这个metadata
# # 以上两种方式根据需要选择其中一种即可
target_metadata = metadata
3、自动生成迁移/版本文件
在 Alembic 中通过 revision 子命令的 --autogrenerate 选项参数来生成自动迁移文件:
# --autogrenerate 选项参数来自动生成迁移脚本
# -m 增加版本信息
alembic revision --autogenerate -m "gen_ddl_by_reflect"
可以看到alembic/versions/文件夹下生成了一个py文件,里面有两个函数用于升级数据库和降级数据库,可根据需要修改。
查看生成的迁移文件
迁移文件中制定了当前版本号
revision
和父版本号down_revision
,以及相应的升级操作函数upgrade
和降级操作函数dwongrade
。在upgrade
和dwongrade
函数中通过相应的 API 来操作 op 和 sa 对象来完成对数据库的修改。
生成的 XXXXversion_comment.py
迁移文件内容:
# 主要是两大操作函数:分别对应了升级和降级操作
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('supplier_attach')
op.add_column('users', sa.Column('test_add_col', mysql.CHAR(length=11), nullable=True, comment='测试增加字段'))
op.alter_column('whitelist_batch', 'uid',
existing_type=mysql.INTEGER(),
nullable=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('whitelist_batch', 'uid',
existing_type=mysql.INTEGER(),
nullable=True)
op.drop_column('users', 'test_add_col')
op.create_table('supplier_attach',
sa.Column('id', mysql.INTEGER(), autoincrement=True, nullable=False),
sa.Column('supplier', mysql.INTEGER(), autoincrement=False, nullable=False),
sa.Column('filename', mysql.CHAR(charset='utf8', collation='utf8_general_ci', length=255), nullable=False),
sa.Column('raw_filename', mysql.CHAR(charset='utf8', collation='utf8_general_ci', length=255), nullable=False),
sa.Column('create_at', mysql.TIMESTAMP(), nullable=True),
sa.PrimaryKeyConstraint('id'),
mysql_default_charset='utf8',
mysql_engine='InnoDB',
mysql_row_format='DYNAMIC'
)
# ### end Alembic commands ###
重要提示:
在进行下一步同步之前,建议先检查一下迁移文件是否如预想一样,否则可能导致表被删除
4、同步数据库
alembic upgrade head
查看数据库,除了迁移的新表,还有一个alembic字典建立的表叫做alembic_version
,只有一个字段和一个值version_num,记录了当前的数据库版本。
其它命令
#查看线上数据库处于什么版本
alembic current
#查看项目目录中的迁移脚本信息
alembic history
#更新数据库
alembic upgrade 版本号
#更新到最新版
alembic upgrade head
#降级数据库
alembic downgrade 版本号
#降级到最初版
alembic downgrade head
#离线更新(生成sql)
alembic upgrade 版本号 --sql > migration.sql
#从特定起始版本生成sql
alembic upgrade 版本一:版本二 --sql >migration.sql
#清除所有生成版本
将version删除, 并删除alembic_version表
报错解决
FAILED: Target database is not up to date.
只修改了model,从没同步数据库,数据库没有记录版本信息,使用alembic stamp head 更新版本记录
另外就是只简单修改了model字段的类型长度等,alembic默认不检测,需要配置compare_type
FAILED: Can’t locate revision identified by ‘1e065e4399bf’
删除数据库中生成的alembic_version表,重新迁移
[SQL: ALTER TABLE whitelist_batch MODIFY uid INTEGER NOT NULL]
同步数据库时,SQL报错,考虑模型定义约束和实际数据库存储不一致,例如这里模型定义nullable=False,实际数据库uid字段却存在null;
Flask-Migrate介绍
Flask-Migrate 的用法和 Alembic 类似,只是将 alembic 换成了你的应用名称「或 flask」+ db 的方式。
Django ORM VS Alembic
- Django ORM
默认migration比较完全自动化,能精确检测model变化, 例如修改一个max_length
,都能准确帮助修改数据库
- Alembic
默认只能检测基本的更改:What does Autogenerate Detect (and what does it not detect?)
参考
https://blog.51cto.com/u_3409716/2904146
https://cloud.tencent.com/developer/article/1536292
https://blog.csdn.net/qq_41637554/article/details/82625598