app context,应用上下文,存储的是应用级别的信息,好比数据库链接信息。python
request context,程序上下文,存储的是请求级别的信息,好比当前访问的urlweb
按照官方文档,有两种方式建立一个app context:
一是当一个请求到来,这时候request context建立,app context也随之建立数据库
二是显式使用 app_context 方法,以下:flask
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
下面跟随代码,看第一种建立是如何实现的。app
咱们知道,一个典型的flask app是这样开始运行的:post
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run()
这时候,app是在特定端口监听请求。测试
当一个请求过来的时候,跟随代码能够知道,会调用Flask class的__call__方法来建立响应this
call的时候,执行以下代码:url
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,以下所示:spa
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/