flask:蓝图(blueprint)、g对象、数据库连接池
一、蓝图(blueprint)
1、蓝图介绍
在Flask中,使用蓝图Blueprint来分模块组织管理。
蓝图实际可以理解为是一个存储一组视图方法的容器对象,其具有如下特点:
- 一个应用可以具有多个蓝图(Blueprint)可以将一个蓝图(Blueprint)注册到任何一个未使用的URL下比如 “/user”、“/goods”
- 蓝图(Blueprint)可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
- 在一个应用初始化时,就应该要注册需要使用的蓝图(Blueprint)
- 但是一个蓝图(Blueprint)并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中
2、蓝图的使用
不用蓝图,划分目录
no_blueprint_flask # 项目名
src #核心源码位置
__init__.py # 包 里面实例化得到了app对象,
models.py #放表模型
views.py # 放视图函数
static # 放静态资源
templates # 放模板
home.html # 模板
manage.py # 启动文件
ps:使用上述结构编写flask项目的时候需要注意,在src的双下init文件中需要导入views.py文件,这样内部的视图函数才会被注册
部分关键文件代码展示
manage.py
from src import app
if __name__ == '__main__':
app.run()
src的双下init.py
from flask import Flask
app = Flask(__name__, template_folder='../templates', static_folder='../static')
app.debug = True
app.secret_key = 'asdfasdjfklewlogsfjaojsifj'
from . import views
3、使用蓝图,划分小型项目目录
蓝图的使用步骤
-第一步:导入蓝图类
from flask import Blueprint
-第二步:实例化得到蓝图对象
us=Blueprint('user',__name__)
-第三步:在app中注册蓝图
app.register_blueprint(us)
-第四步:在不同的views.py 使用蓝图注册路由
@us.route('/login')
-补充:蓝图可以有自己的静态文件和模板
-补充:注册蓝图时,可以使用前缀,必须以/ 开头
使用蓝图,划分小型项目目录
little_blueprint # 项目名
-src # 核心代码
-static # 静态文件
-1.jpg # 图片
-templates # 模板文件
-user.html # 模板
-views # 视图函数存放位置
-order.py # 订单相关视图
-user.py # 用户相关视图
-__init__.py # 包
-models.py # 表模型
-manage.py # 启动文件
部分关键文件代码展示
manage.py
from src import app
if __name__ == '__main__':
app.run()
src的双下init.py
from flask import Flask
app = Flask(__name__)
app.debug = True
app.secret_key = 'asdfasdjfklewlogsfjaojsifj'
from .views.user import user_bp
from .views.order import order_bp
app.register_blueprint(user_bp)
app.register_blueprint(order_bp)
user.py
from src import app
from flask import Blueprint, render_template
user_bp = Blueprint('user', __name__)
@app.route('/user-index')
def user_index():
return 'user-index'
@app.route('/home')
def user_home():
return render_template('home.html', name='zzh')
4、使用蓝图,划分大型项目目录
# 使用蓝图,划分大型项目目录 分成多个app,像django一样
big_blueprint # 项目名
-src # 核心文件
-admin # admin的app
-static # 静态文件
-1.jpg # 图片
-templates # 模板文件目录
-admin_home.html # 模板文件
-__init__.py # 包
-models.py # 表模型
-views.py # 视图函数
-home # home app
-order # orderapp
-__init__.py # 包
-settings.py # 配置文件
-manage.py # 启动文件
ps1:我们在编写大型项目目录的时候把蓝图对象的创建放到了每个app文件夹内的双下init文件中,我们需要在其内部导入包内的views.py文件,执行视图的路由注册
ps2:在注册蓝图对象的时候,可以设置蓝图的路由前缀,使用的属性如下(必须要在前面带上斜杠):
url_prefix='/admin'
部分关键文件代码展示
manage.py
from src import app
if __name__ == '__main__':
app.run()
src的双下init.py
from flask import Flask
app = Flask(__name__)
app.config.from_pyfile('settings.py')
from .admin import admin_bp
# from .home import home_bp
# from .order import order_bp
app.register_blueprint(admin_bp, url_prefix='/admin')
# app.register_blueprint(home_bp, url_prefix='home')
# app.register_blueprint(order_bp, url_prefix='order')
admin app的双下init.py
from flask import Blueprint
admin_bp = Blueprint('admin', __name__, static_folder='static', template_folder='templates')
from . import views
admin app的views.py
from flask import render_template
from . import admin_bp
@admin_bp.route('/home')
def home():
return render_template('/home.html')
5、其他知识点
当我们使用蓝图的时候,可以在每个视图内使用请求扩展,他只作用于当前的视图(用蓝图对象去注册请求扩展方法),如果我们想把请求扩展用于全局,就是使用app(Flask对象)去注册
二、g对象
g对象是专门用来存储用户信息的
g对象的全称的为global,是global的缩写,因为global在python中是个关键字,不能以关键字作为变量名,干脆用了g
g对象在一次请求中的全局(所有地方的代码),都是可以使用的可以放值,可以取值
g 对象相当于一个全局变量,在任意位置导入使用即可
它为什么不学django使用request作为上下文?
- 因为使用request,可能会造成request数据的污染,不小心改了request的属性,但你不知道
- 建议使用g对象,因为他是空的,放入数据之后在当次请求中全局有效
以后想在当次请求中,放入一些数据,后面使用,就可以使用g对象
g和session有什么区别?
- g 是只针对于当次请求
- session针对于多次请求
操作代码
from flask import Flask, g, request
app = Flask(__name__)
app.debug = True
@app.before_request
def before():
if 'home' in request.path:
g.xx = 'xx'
def add(a, b):
# print('---',g.name)
print('---', request.name)
return a + b
@app.route('/')
def index():
print(g.xx)
name = request.args.get('name')
# g.name = name
request.method = name
res = add(1, 2)
print(res)
return 'index'
@app.route('/home')
def home():
print(g.xx)
return 'index'
if __name__ == '__main__':
app.run()
三、数据库连接池
普通方式操作数据库
flask 操作mysql也是使用pymysql模块
from flask import Flask, g, request, jsonify
import pymysql
app = Flask(__name__)
app.debug = True
# @app.route('/article')
# def article():
# # 从mysql的cnblogs库的article表中取出两条数据
# conn = pymysql.connect(user='root',
# password="",
# host='127.0.0.1',
# database='cnblogs',
# port=3306)
# cursor=conn.cursor(pymysql.cursors.DictCursor)
# cursor.execute('select id,title,author_img from aritcle limit 2')
# res=cursor.fetchall()
# print(res)
# return jsonify(res)
# 单例
conn = pymysql.connect(user='root',
password="",
host='127.0.0.1',
database='cnblogs',
port=3306)
cursor = conn.cursor(pymysql.cursors.DictCursor)
@app.route('/article')
def article():
# 从mysql的cnblogs库的article表中取出两条数据
cursor.execute('select id,title,author_img from aritcle limit 2')
res=cursor.fetchall()
print(res)
return jsonify(res)
if __name__ == '__main__':
app.run()
简单使用:在视图函数中,创建pymysql的连接,查数据,查完,返回给前端(来一个请求,创建一个连接,请求结束,连接关闭,djanog就是这么做的)
有什么问题?
- 会出现连接数过多的情况
如果我们把连接对象,做成全局的,在视图函数中,使用全局的连接,查询,返回给前端,就可以解决上面的问题,但是也有其他问题出现了
有什么问题?
- 会出现数据错乱,详见上图
简单来说就是连接对象不能做成单例模式,单例模式下会出现数据混乱(因为要执行下一条查询语句,而来不及返回查询结果)
使用数据库连接池操作数据库
为了解决上面的两个问题,方案如下:
-使用数据库连接池
-创建一个全局的池
-每次进入视图函数,从池中取一个连接使用,使用完放回到池中,只要控制池的大小,就能控制mysql连接数
使用数据库连接池需要安装第三方数据库连接池模块
pip install dbutils
创建数据库连接池对象
ps:需要使用pymysql
from dbutils.pooled_db import PooledDB
import pymysql
pool = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=10, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=3,
# 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制(最好设置一下)
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='',
database='cnblogs',
charset='utf8'
)
在视图函数中导入使用
from flask import Flask, g, request, jsonify
import pymysql
app = Flask(__name__)
app.debug = True
import time
import random
from POOL import pool
# 用池的代码
@app.route('/article_pool')
def article_pool():
conn = pool.connection()
cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.execute('select id,title,author_img from aritcle limit 2')
res = cursor.fetchall()
print(res)
return jsonify(res)
# 不用池的代码
@app.route('/article')
def article():
conn = pymysql.connect(user='root',
password="",
host='127.0.0.1',
database='cnblogs',
port=3306)
cursor = conn.cursor(pymysql.cursors.DictCursor)
time.sleep(random.randint(1,3))
cursor.execute('select id,title,author_img from aritcle limit 2')
res = cursor.fetchall()
cursor.close()
conn.close()
return jsonify(res)
if __name__ == '__main__':
app.run()
压力测试代码
from threading import Thread
import requests
def task():
res = requests.get('http://127.0.0.1:5000/article_pool')
print(len(res.text))
if __name__ == '__main__':
for i in range(500):
t = Thread(target=task)
t.start()
效果:
- 使用池的连接数明显小
- 不使用池连接数明显很大
查看数据库连接数的sql语句
show status like 'Threads%'