Flask-Migrate的使用和常见"坑"的解决办法
1.Flask-Migrate的使用
flask-migrate相关配置:
1 from Application import app 2 from common.models.model import db 3 from flask_migrate import Migrate, MigrateCommand 4 5 migrate = Migrate(app, db, render_as_batch=True, compare_type=True, compare_server_default=True) 6 manager.add_command('db', MigrateCommand) 7 manager.add_command('runserver', Server(host='0.0.0.0'))
8 if __name__ == '__main__':
9 manager.run()
flask-migrate
相关的命令:
python manage.py db init
:初始化一个迁移脚本的环境,只需要执行一次。python manage.py db migrate
:将模型生成迁移文件,只要模型更改了,就需要执行一遍这个命令。python manage.py db upgrade
:将迁移文件真正的映射到数据库中。每次运行了migrate
命令后,就记得要运行这个命令。
2. 常见"坑"的解决办法
第一坑:安装最新版本的flask-migrate插件
首先安装flask-migrate,如果直接pip install flask-migrate安装,默认是安装的是最新版本,这时候你会发现,最新版本和flask的版本会有冲突,导入MigrateCommand时报错。
解决办法:
指定版本安装 ,经过测试:Flask==1.1.2 flask-migrate==2.7.0不会出现问题。
安装完flask-migrate后,为了更好的使用它,我们还得安装另一个很实用的插件flask-script,这是一个项目管理类的插件,可以自定义命令去管理我们的项目。同样的该插件的最新版本,也会和高版本的flask产生冲突,经过测试Flask==1.1.2不会出现问题。
所以说,如果要使用以上两个插件,Flask版本不能太高,否则会出现兼容问题。
第二坑:当使用sqlite时的alter
这次坑的是sqlite,主要是sqlite自己太坑。它不支持drop列,不支持完整的数据库alter语句功能等等。当使用sqlite做migrate和upgrade以及downgrade时,模型有字段的减少或者约束的减少,会报错:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) near "DROP": syntax error [SQL: ALTER TABLE account DROP COLUMN name]
解决方法:
初始化Migrate对象时可以设置其提交类型为批处理 migrate = Migrate(db=db, render_as_batch=True)
以批处理的方式做表结构修改sqlite是支持的,但是批处理本身有安全性的问题。批处理提升了效率,但是丧失了原子性,其中某些sql语句出错我们并不能直接感知到。所以尽量对migrate生成的脚本进行人工检查吧,不怕一万就怕万一。
第三坑:字段长度等属性的修改
FlaskMigrate默认对字段属性的变动是不做检查的。所以当发现256长的A字段的不足以支撑功能,我们需要把它变成1000,修改代码后做migrate会发现提示No changes in schema detected.
解决方法:
上面提到的alembic的源码的EnvironmentContext类内的configure方法里有个sqlalchemy_module_prefix参数旁边还有一些其他的小参数,这次没有坑爹,比较给力,还是贴一下源码吧:
1 def configure( 2 self, 3 connection=None, 4 url=None, 5 dialect_name=None, 6 dialect_opts=None, 7 transactional_ddl=None, 8 transaction_per_migration=False, 9 output_buffer=None, 10 starting_rev=None, 11 tag=None, 12 template_args=None, 13 render_as_batch=False, 14 target_metadata=None, 15 include_name=None, 16 include_object=None, 17 include_schemas=False, 18 process_revision_directives=None, 19 compare_type=False, 20 compare_server_default=False, 21 render_item=None, 22 literal_binds=False, 23 upgrade_token="upgrades", 24 downgrade_token="downgrades", 25 alembic_module_prefix="op.", 26 sqlalchemy_module_prefix="sa.", 27 user_module_prefix=None, 28 on_version_apply=None, 29 **kw 30 ):
这里有一个熟悉的面孔,没错!二号坑里的render_as_batch参数也在这里。同时,还有一个叫做compare_type的参数,默认是False,该参数用于标注是否比较字段属性变化。所以,同二号坑,在初始化Migrate对象的时候加入该参数,如下:
Migrate(db=db, render_as_batch=True, compare_type=True)
第四坑:default默认约束的生成
sqlalchemy的默认值设置有两种方式,第一种是直接的在Column中设置default,这种方式并不在数据库生成默认值约束,是依靠sqlalchemy自身的功能在生成数据的时候生成默认值。另一种是server_default,这种方式是直接在数据库内生成默认值约束。使用较为麻烦,因为server_default参数只接受str或者unicode这种类型,比如Integer字段也需要设置为字符串,时间戳则需要自己转化,较为麻烦。所以,想要在数据迁移时生成数据库的约束,需要使用server_default而不是default。此外,在三号坑内的源码里,也能看见一个熟脸,就是compare_server_default,默认值是False。
解决方法:
1.保证model内所有有默认值的字段的default以server_default形式设置。
2.初始化Migrate对象时做配置:migrate = Migrate(db=db, render_as_batch=True, compare_type=True, compare_server_default=True)
第五坑: flask命令行更新数据库 Error: Target database is not up to date
发现migration的heads和current版本不一致
>python manage.py db heads ca91109159a9 (head) >python manage.py db current 375a1ed11fd8
解决问题
> python manage.py db stamp heads
再次查看magration的heads和current版本发现保持一致!
参考博客:https://blog.csdn.net/c_s_y/article/details/90769675