赞
踩
上下文:相当于一个容器,保存了Flask程序运行过程中的一些信息。
在计算机中,相对于进程而言,上下文就是进程执行时的环境。具体就是各个
Flask中有两种上下文:请求上下文 应用上下文
request 和 session 都属于请求上下文对象。
request:封装了HTTP请求的内容,针对的是http请求。
user = request.args.get('user')
session:用来记录请求会话中的信息,针对的是用户信息。
session['name'] = user.id
session.get('name')
举例请求上下文:
from flask import request,Flask
app = Flask(__name__)
@app.route('/index')
def index():
user_agent = request.headers.get("User-Agent")
return "<p>Your browser is %s</p>" %user_agent
if __name__ == '__main__':
app.run(debug=True)
代码运行结果类似于:
Your browser is Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
Flask的request对象只有在上下文的生命周期内才有效,离开了请求的声明周期,其上下文环境就不存在了,也就无法获取request对象了。
可以使用Flask内部的方法request_contest()来构建一个请求上下文
from flask import Flask
from flask import request
from werkzeug.test import EnvironBuilder
app = Flask(__name__)
ctx = app.request_context(EnvironBuilder("/",'http://localhost/').get_environ())
ctx.push()
try:
print request.url
finally:
ctx.pop()
对于Flask Web应用来说,每一个请求就是一个独立的线程。请求之间的信息要完全隔离,避免冲突,这就需要用到Thread Local
对象是保存状态的地方,在Python中,一个对象的状态都被保存在对象携带的一个字典上。Thread Local是一种特殊的对象,它的状态对线程隔离——也就是说每个线程对一个Thread Local对象的修改都不会影响其他的线程。
此类对象的实现原理:以线程的ID来保存多份状态字典。
在Python中获取Thread Local最简单的方法是使用threading.local()
# coding:utf-8 import threading storage = threading.local() storage.foo = 1 print(storage.foo) # 1 class AnotherThread(threading.Thread): def run(self): storage.foo = 2 print(storage.foo) # 在这个线程中foo已经被修改了 another = AnotherThread() another.start() # 打印2 print(storage.foo) # 但是在主线程里面的值并没有被修改 打印1
只要有Thread Local对象,就能让同一个对象在多个线程下做到状态隔离。
Flask是一个基于WerkZeug实现的框架,因此Flask的App Context和Request Context是基于WerkZeug的Local Stack的实现。
这两种上下文对象类定义在flask.ctx中,ctx.push会将当前的上下文对象压栈压入flask._request_ctx_stack中,这个_request_ctx_stack同样也是个Thread Local对象,也就是在每个线程中都不一样,上下文压入栈后,再次请求的时候都是通过_request_ctx_stack.top在栈的顶端取,所取到的永远是属于自己线程的对象,这样不同线程之间的上下文就做到了隔离。请求结束后,线程退出,ThreadLocal本地变量也随即销毁,然后调用ctx.pop()弹出上下文对象并回收内存。
current_app和g都属于应用上下文
current_app:表示当前运行程序文件的程序实例,可以通过current_app.name打印出当前应用程序实例的名字。
g:处理请求时,用于临时存储的对象,每次请求都会重置这个变量。比如:我们可以获取一些临时请求的用户信息。
1.当调用app = Flask(name) 的时候,创建了程序应用对象app
2.request 在每次http请求发生时,WSGI server调用Flask.call();然后在Flask的内部创建request对象;
3.app的生命周期大于request和g,一个app存活期间,可能发生多次http请求,所以会有多个request和g。
4.最终传入视图函数,通过return、redirect和render_template生成response对象,返回客户端。
从一个Flask App读入配置并且启动开始,就进入了App Context,在其中我们可以配置文件,打开资源文件,通过路由规则反向构造URL。
举栗子:
from flask import Flask,current_app
app = Flask(__name__)
@app.route("/index")
def index():
return "hello %s" %current_app.name
if __name__ == '__main__':
app.run(debug=True)
current_app是一个本地代理,它的类型是werkzeug.local. LocalProxy,它所代理的即是我们的app对象,也就是说current_app == LocalProxy(app)。使用current_app是因为它也是一个ThreadLocal变量,对它的改动不会影响到其他线程。可以通过current_app._get_current_object()方法来获取app对象。current_app只能在请求线程里存在,因此它的生命周期也是在应用上下文里,离开了应用上下文也就无法使用。
和请求上下文一样,可以手动创建应用上下文:
from flask import Flask
from flask import current_app
app = Flask(__name__)
with app.app_context():
print current_app.name
这里的with语句和with open() as f 一样,是Python提供的语法糖,可以为提供上下文环境省略简化一部分工作。这里就简化了其压栈和出栈操作,请求线程创建时,Flask会创建应用上下文对象,并将其压入flask._app_ctx_stack的栈中,然后在线程退出前将其从栈里弹出。
应用上下文也提供了装饰器来修饰hook函数,@teardown_request,它会在上下文生命周期结束前,也就是_app_ctc_stack出栈前被调用。
当 app = Flask(name)构造出一个 Flask App 时,App Context 并不会被自动推入 Stack 中。所以此时 Local Stack 的栈顶是空的,current_app也是 unbound 状态。
在编写离线脚本的时候,如果直接在一个 Flask-SQLAlchemy 写成的 Model 上调用 User.query.get(user_id),就会遇到 RuntimeError。因为此时 App Context 还没被推入栈中,而 Flask-SQLAlchemy 需要数据库连接信息时就会去取 current_app.config,current_app 指向的却是 _app_ctx_stack为空的栈顶。
解决的办法是运行脚本正文之前,先将 App 的 App Context 推入栈中,栈顶不为空后 current_app这个 Local Proxy 对象就自然能将“取 config 属性” 的动作转发到当前 App 上。
那么为什么在应用运行时不需要手动 app_context().push()呢?因为 Flask App 在作为 WSGI Application 运行时,会在每个请求进入的时候将请求上下文推入 _request_ctx_stack中,而请求上下文一定是 App 上下文之中,所以推入部分的逻辑有这样一条:如果发现 _app_ctx_stack为空,则隐式地推入一个 App 上下文。
请求上下文:保存了客户端和服务器交互的数据。
应用上下文:在flask程序运行的过程中,保存的一些配置信息,比如程序文件名,数据库的连接,用户信息等。
1. 既然在 Web 应用运行时里,应用上下文 和 请求上下文 都是 Thread Local 的,那么为什么还要独立二者?
是为了能让两个以上的Flask应用共存在一个WSGI应用中,这样在请求中,需要通过应用上下文来获取当前请求的应用信息。
2. 既然在Web应用运行时中,一个线程同时只处理一个请求,那么 _req_ctx_stack和 _app_ctx_stack肯定都是只有一个栈顶元素的。那么为什么还要用“栈”这种结构?
需要考虑在非Web Runtime的环境中使用的时候,在多个App的时候,无论有多少个App,只要主动去Push它的app context,context stack就会累积起来,这样,栈顶永远是当前操作的 App Context。当一个 App Context 结束的时候,相应的栈顶元素也随之出栈。如果在执行过程中抛出了异常,对应的 App Context 中注册的 teardown函数被传入带有异常信息的参数。
这么一来就解释了这两个问题,在这种单线程运行环境中,只有栈结构才能保存多个 Context 并在其中定位出哪个才是“当前”。而离线脚本只需要 App 关联的上下文,不需要构造出请求,所以 App Context 也应该和 Request Context 分离。
3. App和Request是怎么关联起来的?
(1)Flask实现请求上下文:
# 代码摘选自flask 0.5 中的ctx.py文件,
class _RequestContext(object):
def __init__(self, app, environ):
self.app = app
self.request = app.request_class(environ)
self.session = app.open_session(self.request)
self.g = _RequestGlobals()
(2)Flask中使用_RequestContext的方法如下:
class Flask(object):
def request_context(self, environ):
return _RequestContext(self, environ)
在Flask类中,每次请求都会调用这个request_context函数。这个函数则会创建一个_RequestContext对象,该对象需要接收WerkZeug中的environ对象作为参数。这个对象在创建时,会把Flask实例本身作为实参传进去,所以虽然每次http请求都创建一个_RequestContext对象,但是每次创建的时候传入的都是同一个Flask对象,因此:
由同一个Flask对象相应请求创建的_RequestContext对象的app成员变量都共享一个application
通过Flask对象中创建_RequestContext对象,并将Flask自身作为参数传入的方式实现了多个request context对应一个application context。
然后可以看self.request = app.request_class(environ)这句
由于app成员变量是app = Flask(name) 这个对象,所以app.request_class就是Flask.request_class,而在Flask类的定义中:
request_class = Request
class Request(RequestBase):
....
所以self.request = app.request_class(environ)实际上是创建了一个Request对象。由于一个http请求对应一个_RequestContext对象的创建,而每个_RequestContext对象的创建对应一个Request对象的创建,所以,每个http请求对应一个Request对象。
因此:
作者:馒头白啊白
链接:简书链接
更新于 2018.8.28
什么是上下文上下文相当于一个容器,保存了 Flask 程序运行过程中的一些信息。
Flask 中有两种上下文,请求上下文和应用上下文。
Flask从客户端收到请求时,要让视图函数能访问一些对象,这样才能处理请求。请求对象是一个很好的例子,它封装了客户端发送的HTTP请求。
要想让视图函数能够访问请求对象,一个显而易见的方式是将其作为参数传入视图函数,不过这会导致程序中的每个视图函数都增加一个参数,除了访问请求对象,如果视图函数在处理请求时还要访问其他对象,情况会变得更糟。为了避免大量可有可无的参数把视图函数弄得一团糟,Flask使用上下文临时把某些对象变为全局可访问。
request 和 session 都属于请求上下文对象。
- request:封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get(‘user’),获取的是get请求的参数。
- session:用来记录请求会话中的信息,针对的是用户信息。举例:session[‘name’] = user.id,可以记录用户信息。还可以通过session.get(‘name’)获取用户信息。
当调用app = Flask(name)的时候,创建了程序应用对象app;
request 在每次http请求发生时,WSGI server调Flask.call();然后在Flask内部创建的request对象;
app的生命周期大于request,一个app存活期间,可能发生多次http请求,所以就会有多个request。
最终传入视图函数,通过return、redirect或render_template生成response对象,返回给客户端。
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。
应用上下文对象有:current_app,g
应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:
应用的启动脚本是哪个文件,启动时指定了哪些参数
加载了哪些配置文件,导入了哪些配置
连了哪个数据库
有哪些public的工具类、常量
应用跑再哪个机器上,IP多少,内存多大
current_app.name
current_app.test_value='value
g作为flask程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别
g.name='abc'
请求上下文:保存了客户端和服务器交互的数据
应用上下文:flask 应用程序运行过程中,保存的一些配置信息,比如说程序名,数据库连接,应用信息等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。