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

相關文章
相關標籤/搜索