1.flask-migrate使用背景
在实际的开发中,经常会发生数据库修改的行为,一般我们修改数据库不会直接手动的去修改,而是去修改ORM对应的模型,然后再把模型映射到数据库中,这时候如果有一个工具专门做这种事情,就显得非常有用了,而flask-migrate就是做这个事情的。
flask-migrate是基于alembic进行的一个封装,并且继承到flask中,而所有的迁移操作其实都是alembic做的,他能跟踪模型的变化,并且将变化映射到数据库中。
2.flask-migrate单文件版代码
#! C:\Python\Python36
# -*- coding: utf-8 -*-
# @Time : 2023-01-21 21:55
# @Author : liuchengyong
# @File : test2.py
# @Software: PyCharm
# config
import os
import sys
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
# DB_URI设置
HOSTNAME = '127.0.0.1'
DATABASE = 'class3'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
# app设置
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# migrate配置
db = SQLAlchemy(app)
migrate = Migrate(app,db)
class User(db.Model):
__tablename__ = 'username'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(50))
email = db.Column(db.String(50))
# password = db.Column(db.String(50))
# age = db.Column(db.Integer)
def add_data():
admin = User(name='admin',email='admin@163.com')
guest = User(name='guest',email='guest@163.com')
db.session.add(admin)
db.session.add(guest)
db.session.commit()
@app.route('/')
def index():
return 'lcy122'
if __name__ == '__main__':
db.create_all()
add_data()
app.run()
换言之数据库迁移就是添加和减少字段,将最新的数据库版本号更新了。
在这里新增一个中间程序,存储db的中间程序,在这里只定义db而不进行相关的初始化。
以下为拆分版本
exts.py
from flask_sqlalchemy import SQLAlchemy
#from flask_app import app
db = SQLAlchemy()
在这里注释掉了from flask_app import app
因为如果采用以下写法:
from flask_sqlalchemy import SQLAlchemy
from flask_app import app
# db = SQLAlchemy()
db = SQLAlchemy(app)
就意味着在进行db初始化与赋初值时,exts从flask引用了app,而在引用db是在flask中进行的,进行了循环引用,这样会出问题,所以不对
应该采用第一种写法
models.py
from exts import db
# db database
class User(db.Model):
__tablename__ = 'project_user'
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
name = db.Column(db.String(50))
email = db.Column(db.String(50))
password = db.Column(db.String(50))
age = db.Column(db.Integer)
config.py
HOSTNAME = '127.0.0.1'
# 数据库
# 几栋
DATABASE = 'demo2'
# 端口
# 门牌号
PORT = 3306
# 用户名和密码
# 钥匙
USERNAME = 'root'
PASSWORD = 'root'
DB_URL = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
SQLALCHEMY_DATABASE_URI = DB_URL
SQLALCHEMY_TRACK_MODIFICATIONS = False
flask_app.py
from flask import Flask
import config
from exts import db
from models import User
app = Flask(__name__)
app.config.from_object(config)
# db.create_all() # 这个是干啥的?
db.init_app(app)
@app.route('/')
def index():
return '这是首页'
if __name__ == '__main__':
app.run(debug=True)
manage.py
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from flask_app import app
from exts import db
# 需要映射那个模型,就把哪个模型导入进来
from models import User
manage = Manager(app)
Migrate(app, db)
manage.add_command('db', MigrateCommand)
if __name__ == '__main__':
manage.run()
在这里,入口程序为flask_app
manage.py程序是通过脚本启动的,因为有参数的,如果直接启动manage.py的话会报错,因为这个是命令行方式的入口,有参数的
错误信息:
usage: manage.py [-?] {db,shell,runserver} ...
positional arguments:
{db,shell,runserver}
db Perform database migrations
shell Runs a Python shell inside Flask application context.
runserver Runs the Flask development server i.e. app.run()
optional arguments:
-?, --help show this help message and exit
3.数据迁移操作
在启动flask_app之后,从cmd窗口启动manage.py进行migrate的初始化,在初始化完成之后会生成migrations文件夹,里面的versions文件夹内存在文件名为版本号加下划线的py文件,生成数据库的文件版本号与该版本号为一样的。
初始化:
python manage.py db init
(home) D:\flaskProject\testProject\test2\project_demo>python manage.py db init
Creating directory D:\flaskProject\testProject\test2\project_demo\migrations ... done
Creating directory D:\flaskProject\testProject\test2\project_demo\migrations\versions ... done
Generating D:\flaskProject\testProject\test2\project_demo\migrations\alembic.ini ... done
Generating D:\flaskProject\testProject\test2\project_demo\migrations\env.py ... done
Generating D:\flaskProject\testProject\test2\project_demo\migrations\README ... done
Generating D:\flaskProject\testProject\test2\project_demo\migrations\script.py.mako ... done
Please edit configuration/connection/logging settings in 'D:\\flaskProject\\testProject\\test2\\project_demo\\migrations\\alembic.ini' before proceeding.
数据库迁移:
python manage.py db migrate
(home) D:\flaskProject\testProject\test2\project_demo>python manage.py db migrate
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'project_user'
Generating D:\flaskProject\testProject\test2\project_demo\migrations\versions\cae239fcdbe4_.py ... done
upgrade:
python manage.py db upgrade
(home) D:\flaskProject\testProject\test2\project_demo>python manage.py db upgrade
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade 779828c07852 -> 1ec2bc808ca7, empty message
完成之后查看数据库:
其中的alembic_version记录了最新版本号
查看project_user表格查看到内容
如果需要更改数据表的内容,可以进行后两步操作:
python manage.py db migrate
python manage.py db upgrade
然后可以发现alembic_version中的版本号更新了,数据表的字段也完成了更新
- 中间遇到的问题 错误:Target database is not up to date.
(home) D:\flaskProject\testProject\test2\project_demo>python manage.py db migrate
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
ERROR [root] Error: Target database is not up to date.
查看了博客:
https://blog.youkuaiyun.com/qq_43193386/article/details/99959841
按照其中的方式,
找出versions文件夹内的版本号,去掉文件名最后的下划线后,将该版本号粘贴到数据库中的version_num下,再次进行migrate就可以正常运行了