Flask中current_app出现unbond的原因及解决
问题引出
当使用current_app和request两个本地代理时,有时会出现处于unbond状态而无法使用的情况,本文对此问题的产生和解决作出分析。
Flask的核心机制
各部分解释
- AppContext:应用上下文,对Flask核心对象进行了相关封装
- RequestContext:请求上下文,对Request请求对象进行了相关封装
- _app_ctx_stack:负责存放AppContext的线程隔离栈
- _request_ctx_stack:负责存放RequestContext的线程隔离栈
- current_app:取_app_ctx_stack栈顶的AppContext中的Flask核心对象
- request:取_request_ctx_stack栈顶的RequestContext中的Request对象
请求过程
- 请求进入
- 封装请求对象Request为RequestContext
- 判断_app_ctx_stack是否为空,若为空或没有当前线程对应的AppContext则将app封装成一个AppContext推入栈中,否则不进行任何操作
- 再将RequestContext推入_request_ctx_stack
- 请求处理完毕后,AppContext与RequestContext都弹出栈
(另1:Flask的核心对象app在全局范围内是唯一存在的,而每个线程都有自己对应的AppContext)
(另2:current_app只是app的代理而不是真实的app,可以通过current_app._get_current_object()来获取真实的app)
结论1:_app_ctx_stack和_request_ctx_stack不为空的条件是外界必须有请求进入
结论2:current_app和request取的都是对应栈的栈顶元素,如果外界没有请求进入自然就出现了unbond状态
结论3:当current_app和request在视图函数中使用时一般都没有问题(请求触发),如果在程序的其他地方或者做单元测试时使用则可能出现unbond状态(无请求进入)
解决方案
current_app处于unbond状态的根本原因是_app_ctx_stack中并没有AppContext的存在,解决思路是既然Flask不能在没有请求的情况下将AppContext入栈,那么我们就将其手动推入栈中:
from flask import Flask,current_app app = Flask(__name__) ctx = app.app_context() //生成一个AppContext对象 ctx.push() //手动入栈 # 相关操作代码放在这里 ctx.pop() //手动出栈
在查看相关源码后我们可以发现AppContext其实是一个上下文管理器,于是可以使用python中的with语句来简化操作,with语句中的代码就可以正常使用current_app了:
from flask import Flask,current_app app = Flask(__name__) with app.app_context(): # 相关操作代码放在这里