一 . 上下文 app request 源码解析 g对象
https://www.cnblogs.com/Zzbj/p/10207128.html#autoid-6-0-0
1. app上下文和request上下文
请求上下文和应用上下文的区别
请求上下文(request):保存了客户端和服务器交互的数据。
应用上下文(app):在flask程序运行的过程中,保存的一些配置信息,比如程序文件名,数据库的连接,用户信息等。
应用上下文和请求上下文都是存放在一个‘LocalStack’的栈中,和应用app相关的操作就必须要用到应用上下文,
比如通过current_app获取当前的这个app的名字。和请求相关的操作就必须用到请求上下文,比如使用url_for反转视图函数。
在视图函数中,不用担心上下文的问题,因为视图函数要执行,name肯定是通过访问url的方式执行的,name这种情况下,
Flask底层就已经自动的帮我们把请求上年文和应用上下文都推入到了相应的栈中。
如果想要在视图函数外面执行相关的操作,name就必须要手动推入相关的上下文
手动推入请求上下文:推入请求上下文到栈中,会首先判断有没有应用上下文,如果没有那么就会先推入应用上下文到栈中,
然后再推入请求上下文到栈中
app上下文
from flask import Flask,current_app app = Flask(__name__) #如果在视图函数外部访问,则必须手动推入一个app上下文到app上下文栈中 #第一种方法 # app_context = app.app_context() # app_context.push() # print(current_app.name) #第二种方法 with app.app_context(): print(current_app.name) #context_demo @app.route('/') def index(): # 在视图函数内部可以直接访问current_app.name print(current_app.name) #context_demo return 'Hello World!' if __name__ == '__main__': app.run(debug=True)
请求上下文 from flask import Flask,current_app,url_for app = Flask(__name__) #应用上下文 #如果在视图函数外部访问,则必须手动推入一个app上下文到app上下文栈中 with app.app_context(): print(current_app.name) #context_demo @app.route('/') def index(): # 在视图函数内部可以直接访问current_app.name print(current_app.name) #context_demo return 'Hello World!' @app.route('/list/') def my_list(): return 'my_list' # 请求上下文 with app.test_request_context(): # 手动推入一个请求上下文到请求上下文栈中 # 如果当前应用上下文栈中没有应用上下文 # 那么会首先推入一个应用上下文到栈中 print(url_for('my_list')) if __name__ == '__main__': app.run(debug=True)
2. 源码解析
在视图函数中使用request、session是怎么实现请求的呢?
from flask import Flask,request,session app = Flask(__name__) @app.route('/') def index(): print(request) # <Request 'http://127.0.0.1:5000/' [GET]> print(type(request)) # <class 'werkzeug.local.LocalProxy'> print(session) # <NullSession {}> print(type(session)) # <class 'werkzeug.local.LocalProxy'> return 'ok' if __name__ == '__main__': app.run()
打印可以看出request与session都是属于同一个类,但是打印的结果不一样。那来看看内部实现源码,比如request.args
。request是一个全局变量,点击去可以看到request = LocalProxy(partial(_lookup_req_object, "request"))
同理session也是这个类,就拿request来进行分析:
request是LocalProxy的一个实例对象,参数是local=partial(_lookup_req_object, "request")
是一个偏函数。调用_lookup_req_object
方法,将request当做第一个参数传入。先看看这个偏函数
1.partial(_lookup_req_object, "request")
def _lookup_req_object(name): # 获取top.其实点进去发现_request_ctx_stack=LocalStack(),所以执行的是LocalStack.top()方法,top=ctx,name='request' top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) return getattr(top, name) @property def top(self): try: return self._local.stack[-1] # 取出的是ctx except (AttributeError, IndexError): return None
从当前请求ctx中获得“request”。再看看request.args实际上是从LocalProxy获取args。所以执行的是__getattr__
方法
2. LocalProxy.__getattr__
def __getattr__(self, name): # 在这里name就是args,所以从_get_current_object()去获取 if name == "__members__": return dir(self._get_current_object()) return getattr(self._get_current_object(), name) # self._get_current_object()返回的是request,name是args。所以就是从request中去找args def _get_current_object(self): if not hasattr(self.__local, "__release_local__"): return self.__local() try: return getattr(self.__local, self.__name__) # self.__local里找。而这是实例化中的一个私有属性=传入的参数local=偏函数partial(_lookup_req_object, "request")的返回值=ctx中的request。 except AttributeError: raise RuntimeError("no object bound to %s" % self.__name__)) # object.__setattr__(self, "_LocalProxy__local", local)
3. g对象
专门用来存储用户信息的g对象,g的全称为global。g对象在一次请求中的所有的代码的地方,都是可以使用的
g对象、flash和session的区别
g对象:一旦设置,只能在当前请求中获取,其他的请求都不能获取
flash :一旦设置,可在任意一次请求中获取,但是只能取一次
session:只要设置,在任意请求中都能拿到,无论你拿多少次
代码里边应用到的current_app和g都属于应用上下文对象, 而request就是请求上下文.
current_app 表示当前运行程序文件的程序实例
g: 处理请求时用作临时存储的对象. 每次请求都会重设这个变量 生命周期同RequestContext
request 代表的是当前的请求
那么随之而来的问题是: 这些上下文的作用域是什么?
线程有个叫做ThreadLocal的类,也就是通常实现线程隔离的类. 而werkzeug自己实现了它的线程隔离类: werkzeug.local.Local. 而LocalStack
就是用Local实现的.
# 摘自Flask源码 globals.py from functools import partial from werkzeug.local import LocalStack, LocalProxy _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() current_app = LocalProxy(_find_app) request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session')) g = LocalProxy(partial(_lookup_app_object, 'g'))
g对象是在整个Flask应用运行期间都是可以使用的,并且它也是跟request一样是线程隔离的。
这个对象是专门用来存储开发者自定义的一些数据,方便在整个Flask程序中都可以使用。一般使用就是
,将一些经常会用到的数据绑定到上面,以后就直接从g上面取就可以了,而不是通过传参的形式,这样更加方便。