flask之路【第二篇】蓝图与数据库连接(2)
3.python 连接mysql数据库、建立连接池
def fetchone(sql,params):
conn =pymysql.connect(host='127.0.0.1',port=3306,user='root',passwd='My@20241103',db='test20250609')
cursor=conn.cursor()
cursor.execute(sql,params)
result=cursor.fetchone()
cursor.close()
conn.close()
return result
result=fetchone('select * from user where token=%s',[token,])
每次连接关闭数据库,很浪费时间,浪费资源。可以使用连接池的机制。(池子里有几个已经建立好的连接,可直接使用),连接使用DButils模块可以使用连接池
from dbutils.pooled_db import PooledDB
POOL = PooledDB(
creator=pymysql, # 使用连接数据库的模块
mincached=2, # 初始化时,连接池中至少创建的空闲的连接,0表示不创建
maxcached=3, # 连接池中最多闲置的连接,0和NONE不限制
maxconnections=10, # 连接池最大连接数,0和NONE表示不限制连接数
blocking=True, # 连接池中如果没有可用的连接后,是否阻塞等待。TRUE,等待;False,不等的然后报错
setsession=[], # 开始会话前执行的命令列表,如【"set datestyle to ...","set time zone ..."】
ping=0, # 测试每个连接的连通情况,不用管
host='127.0.0.1', port=3306, user='root', passwd='My@20241103', db='test20250609',charset="utf-8"
)
def fetchone(sql, params):
# conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='My@20241103', db='test20250609')
conn = POOL.connection() #连接池的形式,进行数据库连接。
cursor = conn.cursor()
cursor.execute(sql, params)
result = cursor.fetchone()
cursor.close()
conn.close() #此时不是关闭连接了,而是将连接交还连接池中。
return result
4.redis的安装及建立连接池
- 安装
redis在Windows上直接安装软件Redis-Windows-x64.msi ,然后服务就会运行起来。
有以下命令:redis-server --service-install redis.windows-service.conf --loglevel verbose
redis-server --service-stop
redis-server --service-start
redis-server --service-uninstall
-
Redis修改密码
没有默认密码,设置Redis密码的步骤:找到redis.conf或者redis.windows.conf文件.
在配置文件中找到# requirepass foobared这一行,将其前面的注释符号#去掉。
在requirepass后面写上您想要设置的密码,例如:requirepass yourpassword。
-
客户端访问:
客户端命令是redis-cli.exe
输入ping应会返回pong。
>dbsize 查看当前数据库中key的数目
redis默认16个库0-15,默认使用的0库。可以切换数据库,命令是select index 注:index是0-15中一个
删除当前数据库数据的命令是:
>flushdb
退出客户端使用exit或quit
KEYS * #列出所有键
TYPE mykey #查看键的类型
# 如果类型是 list,则使用 LRANGE 来查看元素
LRANGE kkk 0 -1
#如果类型是 set,则使用 SMEMBERS 来查看成员。
SMEMBERS mykey
#如果类型是 hash,则使用 HGETALL 或 HGET 来查看字段和值。
HGETALL mykey
GET mykey #获得键值
SET mykey "Hello, Redis!" #设置键值对
DEL mykey #删除键
- 用python建立redis连接方法和连接池子的方法。
- 普通连接
REDIS_CONN_PARAMS = {
"host": "localhost",
"port": 6379,
# password":"qwer1234,
"encoding": "utf-8"
}
conn = redis.Redis(**REDIS_CONN_PARAMS)
- 连接池连接
import redis
#创建redis连接池子
Redis_POOL=redis.ConnectionPool(host='127.0.0.1', port=6379,encoding="utf-8",max_connections=1000)
conn = redis.Redis(connection_pool=Redis_POOL)
5.使用redis数据库,建立消息队列缓存
💡如果请求的任务处理过程非常耗时,可以利用redis建立队列,将任务放到任务队列当中去。
-
网站Flask代码功能(以上图片中的API功能):
-
接收请求,生成任务ID,返回给调用者,放到任务队列。
-
查看结果队列,根据iD获取任务的结果
-
# !此文件和worker.py配合使用访问redis键值数据库
# !v6_conn_redis.py
from flask import Flask, request, jsonify
import uuid, redis, json
app = Flask(__name__)
#使用postman访问http://127.0.0.1:5000/task post的字典数据:{"ordered_string":"JKLMNjwxyz0123456789fff"}
@app.route('/task', methods=['POST'])
def task(): # put application's code here
'''
请求的数据格式要求:{"ordered_string":"...."}
token可以通过uuid.uuid4()生成
:return:
'''
ordered_string = request.json.get('ordered_string')
if not ordered_string:
return jsonify({"status": False, "erro": "参数错误"})
# 1.生成任务ID
test_task_id = str(uuid.uuid4())
# 2.放到redis任务队列中去
task_dict = {"tid": test_task_id, "data": ordered_string}
REDIS_CONN_PARAMS = {
"host": "localhost",
"port": 6379,
# password":"qwer1234,
"encoding": "utf-8"
# Redis没有默认密码,设置Redis密码的步骤:找到redis.conf或者redis.windows.conf文件.
# 在配置文件中找到# requirepass foobared这一行,将其前面的注释符号#去掉。
# 在requirepass后面写上您想要设置的密码,例如:requirepass yourpassword。
}
conn = redis.Redis(**REDIS_CONN_PARAMS)
conn.lpush("task_list", json.dumps(task_dict))
# 3.给用户返回
return jsonify({"status": True, "tid": test_task_id, "message": "正在处理任务,预计1分组完成"})
@app.route('/task_result', methods=['GET'])
def task_result(): # put application's code here
# url:/task_result?tid=....
tid = request.args.get('tid')
if not tid:
return jsonify({"status": False, "erro": "参数错误"})
REDIS_CONN_PARAMS = {
"host": "localhost",
"port": 6379,
# password":"qwer1234,
"encoding": "utf-8"
}
conn = redis.Redis(**REDIS_CONN_PARAMS)
sign = conn.hget("result_dict", tid) # 获取字典result_dict={"tid":"sign"}中的签名值
if not sign:
return jsonify({"status": True, "sign_data": "", "message": "任务正在处理,请等待"})
# 此刻得到的sign是二进制数据
sign_string = sign.decode("utf-8")
print(sign_string)
#获取了值后在结果队列中删除这个结果,后续不让取了。
conn.hdel("result_dict", tid)
return jsonify({"status": True, "sign_data": sign_string, "message": "数字签名任务已完成"})
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
- 处理任务worker.py主要实现功能:
- 去任务队列中获取任务执行任务(hashlib数字化签名工作)。
- 将执行结果放到结果队列(hset实现)中去。
'''
去任务队列中获取任务,执行并写入到结果队列
# !此文件和v6_conn_redis.py配合使用访问redis键值数据库
'''
import json
import hashlib
import redis
def get_task():
# 1.redis中获取任务
REDIS_CONN_PARAMS = {
"host": "localhost",
"port": 6379,
# password":"qwer1234,
"encoding": "utf-8"
# Redis没有默认密码,设置Redis密码的步骤:找到redis.conf或者redis.windows.conf文件.
# 在配置文件中找到# requirepass foobared这一行,将其前面的注释符号#去掉。
# 在requirepass后面写上您想要设置的密码,例如:requirepass yourpassword。
}
conn = redis.Redis(**REDIS_CONN_PARAMS)
data = conn.brpop("task_list", timeout=10) # z阻塞等待10秒钟
# return data[1].decode("utf-8") # 因为如果直接返回的是(键,值)元组的二进制形式需要解码。
# 如果是json序列化的字符串,你还可以json.loads得到字典类型
if not data:
return None
return json.loads(data[1].decode("utf-8"))
def set_result(tid, value):
REDIS_CONN_PARAMS = {
"host": "localhost",
"port": 6379,
# password":"qwer1234,
"encoding": "utf-8"
# Redis没有默认密码,设置Redis密码的步骤:找到redis.conf或者redis.windows.conf文件.
# 在配置文件中找到# requirepass foobared这一行,将其前面的注释符号#去掉。
# 在requirepass后面写上您想要设置的密码,例如:requirepass yourpassword。
}
conn = redis.Redis(**REDIS_CONN_PARAMS)
conn.hset("result_dict", tid,
value) # redis.set(key, value): 设置键 key 的值为 value。redis.hset(name, key, value): 在哈希表 name 中设置字段 key 的值为 value
# 上述result_dict是hash表中的名字即字典名,tid是键,value是值。也就是result_dict={tid:value}
def run():
while True:
# 1.获取任务
task_dict = get_task()
print(task_dict)
if not task_dict:
continue
# 2.执行耗时操作即执行work:进行数字化签名
# {"tid": test_task_id, "data": ordered_string}
ordered_string = task_dict["data"]
encrypt_string = ordered_string + "56a5a382-b354-47a4-a0b2-ef40a9420cae"
sign = hashlib.md5(encrypt_string.encode('utf-8')).hexdigest()
# 3.写入结果队列(不用列表了,用redis的hash功能即字典功能)
set_result(task_dict["tid"], sign)
if __name__ == '__main__':
run()
💡优化:
- 以上内容有可以优化的地方,即连接redis数据库,可以把REDIS_CONN_PARAMS参数定义为全局变量。另外也可以利用redis的数据池功能建立连接数据池子。用
Redis_POOL=redis.ConnectionPool(host='127.0.0.1', port=6379,encoding="utf-8",max_connections=1000)
conn=redis.Redis(connection_pool=Redis_POOL)
可以定义一些全局变量,方便后续修改;如:
TASK_QUEUE="task_list"
RESULT_QUEUE="result_dict"
6. 蓝图(Blueprint)用法
💡Flask 蓝图(Blueprint)是一种将大型应用拆分成模块化组件的方式,每个蓝图可以包含独立的路由、模板、静态文件等,最后注册到主应用中。以下是蓝图的详细用法和示例:
6.1 基本使用步骤
(1) 创建蓝图
在模块中定义蓝图(如 auth.py
):
from flask import Blueprint
# 创建蓝图对象:Blueprint('名称', __name__, 参数)
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
(2) 添加路由
@auth_bp.route('/login')
def login():
return "Login Page"
@auth_bp.route('/logout')
def logout():
return "Logout Page"
(3) 注册到主应用
在主应用文件(如 app.py
)中注册蓝图:
from flask import Flask
from auth import auth_bp # 导入蓝图
app = Flask(__name__)
# 注册蓝图(可指定URL前缀)
app.register_blueprint(auth_bp) # 访问路径:/auth/login
6.2 核心功能详解
(1) URL 前缀
- 全局前缀:注册时指定
url_prefix
app.register_blueprint(auth_bp, url_prefix='/user')
- 蓝图内前缀:创建蓝图时指定
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
(2) 蓝图专属静态文件
# 指定蓝图静态文件夹
admin_bp = Blueprint('admin', __name__, static_folder='static_admin')
# 访问:/admin/static_admin/style.css
@admin_bp.route('/static-demo')
def static_demo():
return admin_bp.static_url_path # 输出:/admin/static_admin
(3) 蓝图独立模板目录
# 指定模板文件夹
admin_bp = Blueprint('admin', __name__, template_folder='templates_admin')
@admin_bp.route('/')
def admin_home():
return render_template('admin.html') # 优先查找 templates_admin/admin.html
6.3 项目结构示例
myapp/
├── app.py # 主应用
├── auth/ # 认证蓝图模块
│ ├── __init__.py
│ ├── routes.py # 路由定义
│ └── templates/ # 蓝图专属模板
│ └── auth/
│ └── login.html
├── admin/ # 管理后台蓝图
│ ├── __init__.py
│ └── routes.py
└── templates/ # 全局模板
└── base.html
蓝图文件示例 (auth/routes.py
)
from flask import Blueprint, render_template
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
@auth_bp.route('/login')
def login():
return render_template('auth/login.html') # 路径:auth/templates/auth/login.html
主应用注册 (app.py
)
from flask import Flask
from auth.routes import auth_bp
from admin.routes import admin_bp
app = Flask(__name__)
app.register_blueprint(auth_bp)
app.register_blueprint(admin_bp, url_prefix='/admin')
if __name__ == '__main__':
app.run()
6.4 进阶技巧
(1) 跨蓝图生成 URL
from flask import url_for
# 格式:蓝图名.视图函数名
url_for('auth.login') # 生成 /auth/login
(2) 蓝图请求钩子
@auth_bp.before_request
def check_auth():
if not user_logged_in():
return redirect(url_for('auth.login'))
(3) 蓝图错误处理器
@auth_bp.errorhandler(404)
def auth_404(error):
return "Auth 专用 404 页面", 404
6.5 使用蓝图的优势
- 模块化:按功能拆分应用(如用户认证、博客、后台管理)。
- 代码复用:多个项目共享同一蓝图(如用户系统)。
- 避免路由冲突:通过前缀隔离不同模块的路由。
- 延迟注册:先定义蓝图,最后注册到主应用。
💡通过蓝图,Flask 应用可以轻松扩展,同时保持代码的清晰度和可维护性。