flask中的app context 和 request context

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/

http://flask.pocoo.org/docs/0.12/appcontext/

https://stackoverflow.com/questions/20036520/what-is-the-purpose-of-flasks-context-stacks/20041823#20041823

相关文章
相关标签/搜索