Flask の g オブジェクトの正しい使い方

Flask の g オブジェクトは、リクエスト中にデータを一時的に保持するための仕組みである。正しく使えばコードの見通しがよくなるが、誤用すると問題を引き起こす。

g の基本

g はアプリケーションコンテキストに紐づいたオブジェクトで、任意の属性を設定できる。

from flask import Flask, g

app = Flask(__name__)

@app.route('/')
def index():
    g.user = 'Alice'
    return f'Hello, {g.user}'

g のライフサイクル

g はリクエストごとに新しいオブジェクトが生成される。リクエストが終わると破棄される。

@app.route('/first')
def first():
    g.value = 100
    return 'Set value'

@app.route('/second')
def second():
    # 別リクエストなので g.value は存在しない
    return str(getattr(g, 'value', 'Not found'))  # 'Not found'

典型的なユースケース: データベース接続

リクエスト中に取得したリソースを g に保持し、リクエスト終了時にクリーンアップする。

import sqlite3
from flask import g

def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect('app.db')
    return g.db

@app.teardown_appcontext
def close_db(exception):
    db = g.pop('db', None)
    if db is not None:
        db.close()

@app.route('/users')
def users():
    db = get_db()
    cursor = db.execute('SELECT * FROM users')
    return str(cursor.fetchall())

teardown_appcontext でリクエスト終了時に必ず接続を閉じる。

before_request との組み合わせ

認証済みユーザーを g に設定するパターン。

@app.before_request
def load_user():
    user_id = session.get('user_id')
    if user_id:
        g.user = User.query.get(user_id)
    else:
        g.user = None

@app.route('/profile')
def profile():
    if g.user is None:
        return 'Not logged in', 401
    return f'Hello, {g.user.name}'

g の誤用パターン

# 誤用 1: g をグローバル変数のように使う
# g はリクエストごとにリセットされることを忘れない

# 誤用 2: g をスレッド間で共有しようとする
def background_task():
    print(g.user)  # エラー: コンテキスト外

# 誤用 3: g に大量のデータを詰め込む
g.all_users = User.query.all()  # メモリを無駄に消費

g vs session

gリクエスト内でのみ有効、サーバーサイドに保持
sessionリクエストをまたいで有効、クライアントの Cookie に保存

一時的な計算結果やリソースは g に、ログイン状態など永続化が必要なものは session に保存する。