内容概述:
数据库迁移、
蓝图、
单元测试。
数据库迁移
- 在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库。最直接的方式就是删除旧表,但这样会丢失数据。
- 更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中。
- 在Flask中可以使用Flask-Migrate扩展,来实现数据迁移。并且集成到Flask-Script中,所有操作通过命令就能完成。
- 为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可以附加到flask-script的manager对象上。
Migrate 迁移
在虚拟环境中安装 Migrate :
pip install flask-Migrate
配置命令
Migrate(app, db) # 使用迁移类将应用和数据库连接对象保存起来
manager = Manager(app) # 创建终端命令的对象
manager.add_command(‘db’, MigrateCommand) # 将数据库迁移命令添加到 manager 中
迁移命令
python xxx.py db init # 迁移初始化(生成迁移所需要文件夹 migrations)
python xxx.py db migrate -m “initial” # 生成迁移版本文件
python xxx.py db upgrade # 执行迁移
python xxx.py db history # 查看历史版本
python xxx.py db downgrade 版本号 # 回滚到指定版本
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate, MigrateCommand
# MigrateCommand 就是迁移的命令
from flask_script import Manager
# 总结迁移的命令
# 1.迁移初始化(生成迁移所需要文件夹 migrateions) python xxx.py db init
# 2. 生成迁移版本文件 python xxx.py db migrate -m'inital'
# 3. 执行迁移(向上迁移) python xxx.py db upgrade
app = Flask(__name__)
app.secret_key = 'asdfasdf'
# 配置数据库
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:[email protected]:3306/migratetest'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
# 使用迁移类将应用和数据库连接对象保存起来
Migrate(app, db)
# 创建终端命令的对象
manager = Manager(app)
# 将数据库的迁移命令添加到 manager 中
manager.add_command('db', MigrateCommand)
# 定义模型Role
class Role(db.Model):
# 定义表名
__tablename__ = 'roles'
# 定义列对象
id = db.Column(db.Integer, primary_key=True)
nick_name = db.Column(db.String(64), unique=True)
# 标题
title = db.Column(db.String(64))
user = db.relationship('User', backref='role')
# repr()方法显示一个可读字符串,
def __repr__(self):
return 'Role:'.format(self.name)
# 定义用户
class User(db.Model):
__talbe__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
# 设置外键
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
@app.route('/')
def index():
return 'Hello World!'
if __name__ == '__main__':
manager.run()
蓝图(BluePrint)
简单来说,Blueprint 是一个存储操作方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求。
Flask使用Blueprint让应用实现模块化,在Flask中,Blueprint具有如下属性:
- 一个应用可以具有多个Blueprint
- 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/”、“/sample”或者子域名
- 在一个应用中,一个模块可以注册多次
- Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
- 在一个应用初始化时,就应该要注册需要使用的Blueprint
但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。
BluePrint 蓝图, 就是把以前在视图的函数写到蓝图中去, 相当于对文件进行模块化管理
# 蓝图使用流程
# 导入蓝图类
from flask import BluePrint
# 初始化蓝图
login_blu = BluePrint('login', __name__)
# 使用蓝图注册 url
@login_blu.route('/login/list')
# 注册蓝图
app.register_blueprint(login_blu)
注意:
初始化蓝图的时候,需要赋值访问路径;
注册蓝图时,可以指定一个
urlprefix
关键字参数(这个参数默认是/),在应用最终的路由表url_map
中,在蓝图上注册的路由 URL 自动被加上了这个前缀,这个可以保证在多个蓝图中使用相同的 URL 规则而不会最终引起冲突,只有在注册蓝图时将不同的蓝图挂接到不同的自路径即可。
MVTB 模型:
M:数据库
V:视图
T:template
B:BluePrint 蓝图
- 主文件
main.py
from flask import Flask
from order import order_blu
from cart import cart_blu
app = Flask(__name__)
# 把蓝图注册到 app 上
app.register_blueprint(order_blu)
app.register_blueprint(cart_blu)
@app.route('/')
def index():
return 'Hello World!'
""" 一下代码抽取到 order.py 中
# 订单列表
@app.route('/order/list')
def order_list():
return 'order_list'
"""
@app.route('/usr/info')
def user_info():
return 'user_info'
""" 以下代码拷贝到 cart 模块里面
@app.route('/cart/list')
def cart_list():
return 'cart_list'
"""
if __name__ == '__main__':
print(app.url_map) # url_map 中存储当前函数中所有的路由和视图的对应关系
app.run(debug=True)
- 包文件
__init__.py
from flask import Blueprint
cart_blu = Blueprint('cart', __name__, static_folder="static", template_folder='templates',
url_prefix='/cart')
from .views import *
- 包文件
views.py
from flask import render_template
from . import cart_blu
@cart_blu.route('/list')
def cart_list():
return render_template('cart.html')
单元测试
什么是单元测试:当代码写完之后,检查代码是否符合要求,可以编写测试代码,模拟程序运行的过程,检验代码功能是否符合预期。
在 Web 开发过程中,单元测试实际上就是一些 “断言 (assert)”代码。
断言就是判断一个函数或对象的一个方法所产生的结果是否符合你期望的那个结果。
python 中的 assert 断言是声明布尔值为真的判定,如果表达式为假会发生异常。
常见断言方法
assertEqual # 如果两个值相等,则pass
assertNotEqual # 如果两个值不相等,则pass
assertTrue # 判断bool值为True,则pass
assertFalse # 判断bool值为False,则pass
assertIsNone # 不存在,则pass
assertIsNotNone # 存在,则pass
测试类要继承自
unittest.TestCase
在测试类中定义测试方法
单元测试要以 test 开头
进行测试
# 对数据库进行测试添加和删除
import unittest
from demo3_bookDemo import app, db, Author
class DataBaseTestCase(unittest.TestCase):
# 因为是测试数据的添加和删除, 所以需要单独为测试创建一个 database
# 该方法会首先执行,相当于做测试前的准备工作
def setUp(self):
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql://root:[email protected]:3306/booktest_unittest"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.create_all()
# 该方法会在测试代码执行完后执行,相当于做测试后的扫尾工作
def tearDown(self):
db.session.remove()
db.drop_all()
# 测试代码
def test_add_and_delete_author(self):
author = Author(name='哈赛')
db.session.add(author)
db.session.commit()
# 查询
author = Author.query.filter(Author.name == '哈赛').first()
self.assertIsNotNone(author)
import time
time.sleep(10)
# 删除
db.session.delete(author)
db.session.commit()