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/