Flask には2種類のコンテキストがある。アプリケーションコンテキストとリクエストコンテキストである。これらを正しく理解することで、current_app、g、request、session の挙動が明確になる。
2種類のコンテキスト
| アプリケーションコンテキスト | current_app, g が利用可能 |
| リクエストコンテキスト | request, session が利用可能(app context も含む) |
リクエストコンテキストがプッシュされると、アプリケーションコンテキストも自動的にプッシュされる。
コンテキストのライフサイクル
from flask import Flask, request, g, current_app
app = Flask(__name__)
@app.route('/')
def index():
# リクエストが来ると、Flask が自動的に両コンテキストをプッシュ
print(current_app.name) # アプリケーションコンテキスト
print(request.path) # リクエストコンテキスト
print(g) # アプリケーションコンテキストに紐づく
return 'OK'
# レスポンス後、両コンテキストが自動的にポップされる
コンテキストスタック
Flask は内部的にスタック構造でコンテキストを管理している。
from flask.globals import _app_ctx_stack, _request_ctx_stack
# 複数のアプリを切り替える場合などにスタックが活用される
with app1.app_context():
print(current_app.name) # app1
with app2.app_context():
print(current_app.name) # app2
print(current_app.name) # app1 に戻る
手動でコンテキストをプッシュする
リクエスト外(CLI、テスト、バックグラウンドタスク)ではコンテキストを手動でプッシュする。
# アプリケーションコンテキストのみ
with app.app_context():
print(current_app.config['DEBUG'])
# request は使えない
# リクエストコンテキストも含む
with app.test_request_context('/path?q=test'):
print(request.path) # '/path'
print(request.args['q']) # 'test'
print(current_app.name) # これも使える
current_app vs app
app を直接インポートすると循環インポートの原因になる。current_app を使えば、現在アクティブなアプリケーションを動的に取得できる。
# 悪い例
from myapp import app
def get_config():
return app.config['DEBUG'] # 循環インポートの危険
# 良い例
from flask import current_app
def get_config():
return current_app.config['DEBUG'] # コンテキスト内で呼べば OK
_get_current_object()
LocalProxy から実際のオブジェクトを取得する必要がある場合は _get_current_object() を使う。
# シグナルのハンドラに渡す場合など
real_app = current_app._get_current_object()
コンテキストの理解は Flask の高度な使い方の基礎となる。