app context,应用上下文,存储的是应用级别的信息,比如数据库连接信息。
request context,程序上下文,存储的是请求级别的信息,比如当前访问的url
按照官方文档,有两种方式创建一个app context:
一是当一个请求到来,这时候request context创建,app context也随之创建
二是显式使用 app_context 方法,如下:
from flask import Flask, current_app app = Flask(__name__) with app.app_context(): # within this block, current_app points to app. print current_app.name
下面跟随代码,看第一种创建是如何实现的。
我们知道,一个典型的flask app是这样开始运行的:
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run()
这时候,app是在特定端口监听请求。
当一个请求过来的时候,跟随代码可以知道,会调用Flask class的__call__方法来创建响应
call的时候,执行如下代码:
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) ctx.push() error = None try: try: response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
在执行 ctx.push() 的时候,创建了 app context,如下所示:
app_ctx = _app_ctx_stack.top if app_ctx is None or app_ctx.app != self.app: app_ctx = self.app.app_context() app_ctx.push()
......
_request_ctx_stack.push(self)
调用 self.app.app_context() 的时候,将之前Flask创建的app作为参数传入到 AppContext 类里。
app_ctx.push() 的时候,将返回的app_ctx对象放到 _app_ctx_stack 这个stack上
显然,这里的app中包含了许多应用层面的信息。可以在flask.app.py:Flask这个类中看到。
比如 default_config 这个ImmutableDict中就包含了诸如 SECRET_KEY,JSONIFY_MIMETYPE这些应用层面的信息。
随后调用 _request_ctx_stack.push(self),将RequestContext对象放到_request_ctx_stack这个stack上
那么这个RequestContext对象(也就是上文wsgi_app方法中的ctx)中包含什么呢?
一路跟上去,发现初始化ctx对象的environ是从werkzeug.serving.py:WSGIRequestHandler:make_environ
这个方法得到的,这里面都是一些与请求相关的信息,比如:QUERY_STRING,REQUEST_METHOD,REMOTE_PORT等等。
示例:https://www.toptal.com/python/pythons-wsgi-server-application-interface
有一个问题,为什么要区分app context和request context?原因如下:
1 首先概念上,逻辑上讲,在request未到来之前,app处于app context,当request到来之后,request context被创建。
app context用于存储数据库链接等与app相关的信息,request context用于存储和request相关的信息。
这是一个很自然的想法
2 flask允许多app并用(http://flask.pocoo.org/docs/0.12/patterns/appdispatch/),
request需要不同的app context来区分自己正在那个app上请求
3 测试的时候(这是一种在非web环境运行Flask代码的情况,一般只在主线程进行),离线脚本只需要app关联的上下文,
不需要构造请求,此时只需要app context,不需要 request context,所以需要区分
一个来自stackoverflow的例子:
app = Flask(__name__) db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object db.init_app(app)
在这种情况下,初始化、测试db的时候只需要一个app context就行了,不需要request context
为什么用stack来存储app context和request context?
首先,为什么request context设计成stack的形式?
flask请求中有redirect的情况。
比如当请求a的时候,a需要再请求b,这时就可以把请求push,处理b的请求之前把与b有关的请求信息push,等请求完b再处理a
显然stack最合适
app context为什么设计成stack的形式?
前文已经解释过,既然flask允许多个app并存,当request在不同的app context之间游走的时候,用stack记录哪个是“当前”自然是最好的
一旦脱离了某个app context的范围,app context自然就出栈了
一个源自官网的例子:
from flask import Flask, current_app app = Flask(__name__) with app.app_context(): # within this block, current_app points to app. print current_app.name
ref: http://cizixs.com/2017/01/13/flask-insight-context
https://blog.tonyseek.com/post/the-context-mechanism-of-flask/
http://flask.pocoo.org/docs/0.12/appcontext/