Loading

hellohelp

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的安装及建立连接池

  1. 安装

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                      #删除键
  1. 用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建立队列,将任务放到任务队列当中去。

image-20250612172922777

  1. 网站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)

  1. 处理任务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()

💡优化:

  1. 以上内容有可以优化的地方,即连接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)
  1. 可以定义一些全局变量,方便后续修改;如:

    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 使用蓝图的优势

  1. 模块化:按功能拆分应用(如用户认证、博客、后台管理)。
  2. 代码复用:多个项目共享同一蓝图(如用户系统)。
  3. 避免路由冲突:通过前缀隔离不同模块的路由。
  4. 延迟注册:先定义蓝图,最后注册到主应用。

💡通过蓝图,Flask 应用可以轻松扩展,同时保持代码的清晰度和可维护性。

posted @ 2025-07-15 13:02  HordorZzz  阅读(17)  评论(0)    收藏  举报