アプリケーションコンテキストについてだいぶイメージがついてきました。
コンテキストについて理解が進むと自分でテストプログラムもガンガンかけそうだね〜
前回に引き続き、flaskのアプリケーションコンテキストについてのチュートリアルを読み進めています。読み進めて理解した内容を私の切り口でご紹介します。今回は、「Manually Push a Context」の項目から進めていきます。
進めているチュートリアルページはこちらになります。
こんな人の役に立つかも
・PythonでWebアプリケーションを作成したい人
・flaskのチュートリアルを行なっている人
・flaskのコンテキストについてイメージを深めたい人
アプリケーションコンテキストを手動で利用する
一般的に、アプリケーションコンテキストはflaskがリクエストの処理を開始した時にスタックにPUSHされて「current_app」という属性にアクセスできるようになります。テスト用のプログラムで擬似的にクライアントからリクエストを出す場合、手動でアプリケーションコンテキストをスタックにPUSHする必要があります。(手動で利用する=スタックにPUSHする、という意味で言葉を利用しています。)
アプリケーションコンテキストを利用する具体的なプログラムとしてはwithブロックを利用して次のような形で利用します。
with app.app_context():
#コンテキスト利用可能
#ここでcurrent_appを利用
チュートリアルでは、テスト用のプログラムでしか使っていないですね。
アプリケーションコンテキストという概念があってcurrent_appが使えているというイメージが大切かも
データを保持する
アプリケーションコンテキストと同じ期間保持されるオブジェクトに、「g」オブジェクトがあります。
時々出現する黒いあいつではないですね。
「g」オブジェクトは、コンテキストの間のグローバル変数という位置付けのオブジェクトらしいです。
そのため、リクエストをまたいで値を保持しておく用途には利用できません。リクエストをまたいで値を保持する場合は「session」を利用します。
gオブジェクトの利用例
データベースのコネクション
「gオブジェクト」は主に、リクエストの中で複数の関数にアクセスされる可能性のあるデータを格納しておくようなオブジェクトです。
主に、データベースへの接続の例としては次のパターンで利用できるとのことです。
from flask import g
def get_db():
if 'db' not in g:
g.db = connect_to_database()
return g.db
@app.teardown_appcontext
def teardown_db():
db = g.pop('db', None)
if db is not None:
db.close()
get_dbという関数にて、dbがgに存在していなければ、g.dbにデータベース情報を入れます。ここでの「connect_to_database()」は実際にはチュートリアルでは次のようなSQLite3の処理が入っていました。
g.db = sqlite3.connect(
current_app.config['DATABASE'],
detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
そして、データベースはコネクションのクローズも大切です。「@app.teardown_appcontext」にてコンテキストが終了した時にデータベース情報をgから取り除くような処理を行っています。
g.popの挙動
gのpopメソッドでgオブジェクトから「db」のデータを取り出し、取り除きます。「g.db」が存在していない場合、「変数db」にはNoneを返します。公式ドキュメントでは、g.popメソッドはdictのpopメソッドのような挙動、と説明されています。
チュートリアルでは、「@app.teardown_appcontext」とするやり方ではなく次のようにdb.pyのinit_app関数でコンテキスト終了時にclose_dbするように登録されています。
def init_app(app):
#このように登録する方法もあります。
app.teardown_appcontext(close_db)
そして、db.pyのinit_appはアプリケーションファクトリであるcreate_appで呼び出されていますので、コンテキストの終了時に同様に処理されることとなります。
userのIDの取得と格納
コンテキストの中で色々な関数から呼び出されるデータとして、ユーザーのIDがあります。これも、チュートリアルでは次のように利用されていました。
「auth.py」の一部です。
@bp.before_app_request
def load_logged_in_user():
user_id = session.get('user_id')
if user_id is None:
g.user = None
else:
g.user = get_db().execute(
'SELECT * FROM user WHERE id = ?', (user_id,)
).fetchone()
ここでも、g.userという形でgオブジェクトにコンテキスト中に利用できるグローバルなuserというデータを格納しています。
userIDはデータベースのようにコネクションをクローズするような処理は必要ないので、そのままコンテキスト終了と共にgが破棄されて消えるようになっているようです。