Flask中的上下文的概念是Flask中非常精妙的一个设计, 通过上下文将请求和应用中需要的内容, 封装起来并且使得内容做到线程隔离. 理解这个机制首先从本地线程讲起.
本地线程
import threading
mydata = threading.local()
mydata.number = 42
print(mydata.number)
log = []
def f():
mydata.number = 11
log.append(mydata.number)
thread = threading.Thread(target=f)
thread.start()
thread.join()
print(log)
print(mydata.number)
输出:
(flask) ➜ pyWeb python ch3_local.py
42
[11]
42
本地线程: 不同线程对内容的修改只在线程内部发挥作用. 子线程和父线程中的变量不会相互影响, 原理是:在threading.current_thread().__dict__
里添加一个包含对象mydata
的id值的key来保存不同线程的状态
Wekzeug中的local
Wekzeug对本地线程进行了自己的实现, 相比threading.local
有以下区别:
- 使用
__storage__
保存不同线程的状态 - 提供
release_local
方法释放本地线程的变量. - Wekzeug实现了两种与本地线程相关的数据结构
- LocalStack: 基于werkzeug.local.Local实现的栈结构
- LocalProxy: 构造此结构时接受一个可以调用的参数(通常是函数), 这个函数执行后就是通过LocalStack实例化的栈的栈顶对象. 对于LocalProxy对象的操作实际上都会转发到这个栈顶对象.
flask.request
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
name = request.args.get('name')
以上例子中 name变量引用了request但是这时候并没有args对象, 直到有请求进来时, 才有name内容. 事实上, request就是一个localProxy
, 上述代码的实际过程是:
- 用户访问发生请求
- 发生请求的过程中向`_request_ctx_stack推入这个请求上下文的对象, 它会变成栈顶. request就会指向这个请求上下文.
- 在视图函数中使用request就能够使用上下文中的
args.get('name')
了.
这样做的好处是: 首先线程独立, 第二, 通过代理把一些局部对象变成全局可访问.
使用上下文
应用上下文和请求上下文的关系
应用上下文缓存发生在请求之前要使用到的资源, 请求上下文通常储存本次请求包含的上下文, 例如请求包含的参数和数据. 请求上下文发生在HTTP请求开始, WSGI Server调用Flask.__call__()
之后. 两者之间的关系:
class RequestContext(object):
self._implicit_app_ctx_stack = []
def push(self):
# some stuff
app_ctx = _app_ctx_stack.top
if app_ctx is None or app_ctx != self.app:
app_ctx = self.app.app_context()
app_ctx.push()
self._implicit_app_ctx_stack.append(app_ctx)
# some other stuff
def pop(self, exc = _sentinel):
app_ctx = self._implicit_ctx_stack.pop()
try:
# some stuff
finally:
# some other stuff
if app_ctx is not None:
app_ctx.pop(exc)
在应用环境中, 请求上下文和应用上下文是一一对应的, 请求上下文和应用上下文都是本地线程.
Flask中的上下文变量
-
flask.current_app
: 应用上下文, 当前app实例对象 -
flask.g
: 应用上下文. 处理请求时用作临时存储的对象 -
flask.request
: 请求上下文. 封装了客户端发出的HTTP请求中的内容 -
flask.session
: 请求上下文: 存储了用户会话.
为什么要区分应用上下文和请求上下文
- 使用
DispatcherMiddleware
, 支持多个app共存. - 非Web模式下. 一个应用上下文可以对应多个请求上下文.