Falsk五
请求扩展
before_request:请求来了会走,依次从上往下执行,但是如果其中一个返回了响应对象,后续就不走了,视图函数也不走
after_request: 请求走了,会从下往上依次执行,必须返回相应对象
假设:
写了3个before_request 第二个返回了响应对象
写了3个after_request 所有的after_request都会执行,从下往上
使用场景:
before_request 做一些拦截,判断
比较校验请求头中是否带token,user-agent
after_request在响应中加入
响应头中
响应cookie
修改相应体
before_first_request 老版本:新版本没有了,项目启动后第一次执行,后面就再不执行了
teardown_request
每个请求之后绑定一个函数,即使遇到异常
@app.teardown_request def lqz(err): print(err) # 如果视图函数正常顺利运行,err是None的,如果视图函数出错了,err就是错误对象,一般用来做日志记录 print('teardown_request')
errorhandler
路径不存在时404,服务器内部错误500
@app.errorhandler(404) # 路径不存在 def error(arg): print('xxx') print(arg) return render_template('404.html')
@app.errorhandler(500) # 服务器内部错误 def error(arg): return render_template('500.html')
标签:
temple_global
@app.template_global() def sb(a1, a2): return a1 + a2 #{{sb(1,2)}}
过滤器:
template_filter
@app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3 #{{ 1|db(2,3)}}
蓝图
blueprint 翻译过来的:flask是要做成项目,放到多个文件夹中,使用蓝图划分目录
不使用蓝图划分目录:
使用蓝图,划分目录
大型项目
小型项目
蓝图使用步骤:
1、在不同的视图函数中初始化蓝图
bp_user = Blueprint('user', __name__)
2、以后注册路由,写请求扩展,统一都用蓝图做(局部的,如果使用app写扩展请求,这是全局的)
@bp_user.before_request def before(): print('user的---》before') @bp_user.route('/login') def login():
3、在app中注册蓝图
app.register_blueprint(bp_home)
from flask_session import Session
from redis import Redis
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1', port='6379')
# app.config['SESSION_KEY_PREFIX'] = 'lqz' # 如果不写,默认以:SESSION_COOKIE_NAME 作为key
# app.config.from_pyfile('./settings.py')
Session(app) # 核心跟第一种方式一模一样
注意两个点
session的前缀如果不传,默认:
config.setdefault('SESSION_KEY_PREFIX', 'session:')
session的key理应该是 uuid,但是咱们还是三段式--》之前浏览器器中存在了
数据库连接池
flask使用pymysql
@app.route('/article') def article(): import pymysql from pymysql.cursors import DictCursor conn = pymysql.connect(user='root', password="lqz123?", host='127.0.0.1', database='cnblogs', port=3306) cursor = conn.cursor(DictCursor) # 获取10条文章 sql = 'select id,name,url from article limit 10' cursor.execute(sql) # 切换 res = cursor.fetchall() print(res) return jsonify({'code': 100, 'msg': '成功', 'result': res})
关于jsonify:Flask 使用json或者jsonify返回json响应数据的方法-腾讯云开发者社区-腾讯云 (tencent.com)
存在问题
1、原生pymsql操作,最好有个orm
2、并发问题:conn和cursor要做成单例,还是每个视图函数一个?
如果用单例,数据会错乱
咱们需要,每个视图函数拿一个连接,如果并发数过多,,mysql连接数就很多,使用连接池解决
解决上面两个问题
数据库连接池
创建一个全局池
每次进入视图函数,从池中取一个连接使用,使用完放回到池中,只要控制池的大小,就能控制mysql连接数
使用第三方数据库连接池,使用步骤:
1、安装pip install dbutils
2、使用:实例化得到一个池对象---》池是单例
from dbutils.pooled_db import PooledDB import pymysql POOL = PooledDB( creator=pymysql, # 使用链接数据库的模块 maxconnections=6, # 连接池允许的最大连接数,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='lqz123?', database='cnblogs', charset='utf8' )
3、在视图函数中导入使用
@app.route('/article') def article(): conn = POOL.connection() cursor = conn.cursor(DictCursor) #直接返回字典格式 # 获取10条文章 sql = 'select id,name,url from article limit 10' cursor.execute(sql) time.sleep(1) # 切换 res = cursor.fetchall() print(res) return jsonify({'code': 100, 'msg': '成功', 'result': res})
池版
@app.route('/article') def article(): conn = POOL.connection() cursor = conn.cursor(DictCursor) # 获取10条文章 sql = 'select id,name,url from article limit 10' cursor.execute(sql) time.sleep(1) # 切换 res = cursor.fetchall() print(res) return jsonify({'code': 100, 'msg': '成功', 'result': res})
非池版
@app.route('/article') def article(): import pymysql from pymysql.cursors import DictCursor conn = pymysql.connect(user='root', password="lqz123?", host='127.0.0.1', database='cnblogs', port=3306) cursor = conn.cursor(DictCursor) # 获取10条文章 sql = 'select id,name,url from article limit 10' cursor.execute(sql) # 切换 time.sleep(2) res = cursor.fetchall() print(res) return jsonify({'code': 100, 'msg': '成功', 'result': res})
关于fetchall和fetchone
如果select本身取的时候有多条数据时:
cursor.fetchone():将只取最上面的第一条结果,返回单个元组如(‘id’,‘title’),然后多次使用cursor.fetchone(),依次取得下一条结果,直到为空。
cursor.fetchall() :将返回所有结果,返回二维元组,如((‘id’,‘title’),(‘id’,‘title’)),
如果select本身取的时候只有一条数据时:
cursor.fetchone():将只返回一条结果,返回单个元组如(‘id’,‘title’)。
cursor.fetchall() :也将返回所有结果,返回二维元组,如((‘id’,‘title’),),
python在mysql在使用fetchall或者是fetchone时,综合起来讲,fetchall返回二维元组(元组中含有元组),fetchone只返回一维元组
压测代码 jmeter工具 ---》Java
import requests
from threading import Thread
没有连接池
# 没有连接池 def task(): # res = requests.get('http://127.0.0.1:8888/article') res = requests.get('http://127.0.0.1:8889/article') print(res.json()) if __name__ == '__main__': l = [] for i in range(100): t = Thread(target=task) t.start() l.append(t) for i in l: i.join()
效果是:
使用池的连接数明显小
不使用池的连接数明显很大
查看数据库连接数
show status like %Threads%。
django的mysql操作,没有连接池,一个请求就是一个新的连接
django中引入连接池,自行搜索
flask后期,使用sqlalchemy,自带连接池,这个不需要咱们处理了
from flask_script import Manager manager = Manager(app) if __name__ == '__main__': manager.run()
5、自定制命令
@manager.command def custom(arg): """自定义命令 python manage.py custom 123 """ print(arg)
6、执行自定制命令
python manage.py custom 123
具体使用
如果运行不了:报错 from flask._compat import text_type,降版本
1 pip3 install Flask-Script==2.0.3
2 pip3 install flask==1.1.4
3 pip3 install markupsafe=1.1.1
from flask import Flask from flask_script import Manager app = Flask(__name__) app.debug = True manager = Manager(app)
自定制命令
@manager.command def custom(arg): """自定义命令 python manage.py custom 123 """ print(arg)
@manager.option('-n', '--name', dest='name') @manager.option('-u', '--url', dest='url') def cmd(name, url): """ 自定义命令(-n也可以写成--name) 执行: python manage.py cmd -n lqz -u xxxx 执行: python manage.py cmd --name lqz --url xxx """ print(name, url)
比如定制一个命令---》导入初始化的省市的数据
比如定制一个命令---》传入一个excel---》把excel的数据同步到mysql表中---》openpyxl
新版本启动了(基于click)
flask自己有,基于click
运行flask flask --app py文件名字:app run
使用步骤:
1、flask 运行项目flask --app py文件名字:app run
2、定制命令
@app.cli.command("create-user") @click.argument("name") def create_user(name): print(name)
命令行中执行
flask --app 7-flask命令:app create-user lqz
# 简写成 前提条件是 app所在的py文件名字叫 app.py
flask create-user lqz
django中自定制命令
1、app下新建文件夹
management/commands/
2、在该文件夹下新建py文件,随便命名(命令名)
3、在py文件中写代码
from django.core.management.base import BaseCommand class Command(BaseCommand): help = '命令提示' def handle(self, *args, **kwargs): 命令逻辑
4、使用命令
python manage.py py文件(命令名)
信号
https://flask.palletsprojects.com/en/3.0.x/signals/
Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为
内置信号
request_started = _signals.signal('request-started') # 请求到来前执行 request_finished = _signals.signal('request-finished') # 请求结束后执行 before_render_template = _signals.signal('before-render-template') # 模板渲染前执行 template_rendered = _signals.signal('template-rendered') # 模板渲染后执行 got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行 request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否) appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 应用上下文执行完毕后自动执行(无论成功与否) appcontext_pushed = _signals.signal('appcontext-pushed') # 应用上下文push时执行 appcontext_popped = _signals.signal('appcontext-popped') # 应用上下文pop时执行 message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发
通过内置信号,可以实现跟请求扩展类似的功能,但是他们是不同的方案,信号更丰富
signal:信号--》flask中的信号,django中也有
Semaphore :信号量,多把锁
https://zhuanlan.zhihu.com/p/489305763
import threading def task(semaphore): # 请求访问资源 semaphore.acquire() try: # 访问共享资源 print("Accessing shared resource") finally: # 释放资源 semaphore.release() # 创建Semaphore对象 semaphore = threading.Semaphore(5) # 创建50个线程 threads = [threading.Thread(target=access_resource, args=(semaphore,)) for _ in range(50)] # 启动线程 for thread in threads: thread.start() # 等待所有线程结束 for thread in threads: thread.join()
内置信号使用
内置信号机制实现
第一步:写一个函数
def render_logger(*args, **kwargs): print(args) print(kwargs) # kwargs.get('template') app.logger.info('模板渲染了')
第二步:跟内置信号绑定
signals.template_rendered.connect(render_logger)
第三步:正常操作,等信号触发
自定义信号
自定义信号
第一步:定义自定义信号
from flask.signals import _signals
print_args = _signals.signal('print_args')
第二步:写一个函数
def lqz(*args, **kwargs): print(args) print(kwargs) print('我执行了')
第三步:绑定信号
print_args.connect(lqz)
第四步:触发信号
@app.route('/home') def home(): print('xxxxxxxxx') # 第四步:触发信号 print_args.send(value='xxxxxxxxx') return 'home'
信号的实际用途
1、新增一个用户,给管理员发条邮件
2、banner表中插入数据,就删除缓存
3、只要mysql增加一条数据,就把数据同步到es中
django中如何使用信号
banner表中插入数据,就删除缓存 伪代码
1、定义一个函数
from django.db.models.signals import pre_save def callBack(*args **kwargs): print(args) print(kwargs) instance=kwargs.get('instance') # 新增的对象 # 通过对象,判断是增加的哪个表中的数据 #删缓存
2、跟内置信号绑定
pre_save.connect(callBack)
3 等待内置信号触发
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能