flask-mysqldb数据库迁移
数据库迁移
在开发程序的过程中,你会发现有时需要修改数据库模型,而且修改之后还需要更新数据库。仅当数据库表不存在时,Flask-SQLAlchemy 才会根据模型进行创建。因此,更新表的唯一方式就是先删除旧表,不过这样做会丢失数据库中的所有数据。更新表的更好方法是使用数据库迁移框架。源码版本控制工具可以跟踪源码文件的变化,类似地,数据库迁移框架能跟踪数据库模式的变化,然后增量式的把变化应用到数据库中。
SQLAlchemy 的主力开发人员编写了一个迁移框架,称为Alembic(https://alembic.readthedocs.org/en/latest/index.html)。除了直接使用Alembic 之外,Flask 程序还可使用Flask-Migrate(http://flask-migrate.readthedocs.org/en/latest/)扩展。这个扩展对Alembic 做了轻量级包装,并集成到Flask-Script 中,所有操作都通过Flask-Script 命令完成。
在虚拟环境中安装Flask-Migrate和flask-script等扩展
pip install flask-migrate
pip install flask-script
创建一个migrate实例,将应用和数据库操作句柄联系在一起
migrate = Migrate(app, db)
在你所创建的xx.py文件中配置以下文件
#flask_script作用是用命令行来管理
from flask_script import Manager
#flask_migrate作用是用来迁移数据库
from flask_migrate import Migrate, MigrateCommand#MigrateCommand迁移指令
from flask_sqlalchemy import SQLAlchemy
# #用于连接数据库的URI 数据库类型 账号密码 ip 端口 数据库名
app.config['SQLALCHEMY_DATABASE_URI']='mysql://root:mysql@127.0.0.1:3306/flask'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True#这一步非常耗性能(开启追踪)
app.config['DEBUG'] = True
# 创建一个管理数据库对象(句柄),注意参数需要和app产生联系
db = SQLAlchemy(app) #SQLAlchemy语言的映射关系
manager = Manager(app, db)
# 第一个参数是Flask-migrate的实例,第二个参数是Sqlalchemy数据库实例,将应用和数据库操作句柄联系在一起
migrate = Migrate(app, db)
# manager是Flask-Script的实例,在其中增加迁移命令,并且起别名db
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':
manager.run()
如果执行migrate有报错,信息如下
你需要把你生成的迁移文件的文件夹给删除,再把数据库中的alembic_version表给删除
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
ERROR [root] Error: Can't locate revision identified by '2b8b9595ef38'
以下操作皆在终端中进行
创建迁移仓库
这个命令会创建migrations文件夹,所有迁移文件都放在里面。
python database.py db init
创建迁移脚本
自动创建迁移脚本有两个函数
upgrade():函数把迁移中的改动应用到数据库中。
downgrade():函数则将改动删除。
自动创建的迁移脚本会根据模型定义和数据库当前状态的差异,生成upgrade()和downgrade()函数的内容。
对比不一定完全正确,有可能会遗漏一些细节,需要进行检查
迁移数据库
python xx.py db migrate -m 'initial migration'
更新数据库
python database.py db upgrade
查看以前的版本
可以根据history命令找到版本号,然后传给downgrade命令:
python xx.py db history
输出格式: -> 版本号 (head), initial migration
回滚到指定版本
python app.py db downgrade 版本号
实际操作顺序:
迁移数据库一般情况下前三步就够了
1.python xx.py db init
2.python xx.py db migrate -m"版本名(注释)"
3.python xx.py db upgrade 然后观察表结构
4.根据需求修改模型
5.python xx.py db migrate -m"新版本名(注释)"
6.python xx.py db upgrade 然后观察表结构
7.若返回版本,则利用 python xx.py db history查看版本号
8.python xx.py db downgrade(upgrade) 版本号
整体代码如下:
from flask import Flask,render_template,request
#flask_script作用是用命令行来管理
from flask_script import Manager
#flask_migrate作用是用来迁移数据库
from flask_migrate import Migrate, MigrateCommand #MigrateCommand迁移指令
from flask_sqlalchemy import SQLAlchemy
import os
app = Flask(__name__)
#生成秘钥
app.secret_key = os.urandom(24)
# #用于连接数据库的URI 数据库类型 账号密码 ip 端口 数据库名
app.config['SQLALCHEMY_DATABASE_URI']='mysql://root:mysql@127.0.0.1:3306/flask'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False#这一步非常耗性能(开启追踪)
app.config['DEBUG'] = True
# 创建一个管理数据库对象(句柄),注意参数需要和app产生联系
db = SQLAlchemy(app) #SQLAlchemy语言的映射关系
manager = Manager(app)
# 第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app, db)
# manager是Flask-Script的实例,这条语句在flask-Script中添加一个db_command命令
manager.add_command('db', MigrateCommand)
#角色和用户之间,一对多,一个角色可以对应多个用户,一个用户只有一个角色
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(20),unique=True)
#在主表加关系,与用户表产生联系,代表的是这个角色下面所有的用户
# 用到的时候加载关联对象
users = db.relationship('Users',backref='role',lazy='dynamic')
#加载完成对象之后,立即加载关联对象
#users = db.relationship('Users',backref='role',lazy='subquery')
def __repr__(self):
return'<Role %s>' %self.name
# 创建用户类
class Users(db.Model):
#定义表名
__tablename__ = 'users'
#定义列对象
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(20),unique=True)
is_delete = db.Column(db.Boolean,default=False)
email = db.Column(db.String(30),nullable=True)
#外键,添加外键的时候需要注意,指定表明和字段
role_id = db.Column(db.Integer,db.ForeignKey('roles.id'))
# 这个是返回可读字符串
def __repr__(self):
return '<Users %s>' % self.name
if __name__ == '__main__':
manager.run()