Flask でカスタムデコレータを作成する
Flask でカスタムデコレータを作成すると、認証、権限チェック、ロギングなどの共通処理を再利用可能な形で実装できる。
基本的なデコレータ
from functools import wraps
from flask import session, redirect, url_for
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
@app.route('/dashboard')
@login_required
def dashboard():
return 'Dashboard'@wraps(f) を使うことで、元の関数名やドキュメントが保持される。これがないと endpoint が decorated_function になってしまう。
引数を取るデコレータ
def role_required(role):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if g.user is None or g.user.role != role:
return 'Forbidden', 403
return f(*args, **kwargs)
return decorated_function
return decorator
@app.route('/admin')
@login_required
@role_required('admin')
def admin_panel():
return 'Admin Panel'外側から role_required('admin') → login_required → admin_panel の順で適用される。実行時は内側から外側へ評価される。
デコレータの順序
@app.route('/path')
@decorator_a
@decorator_b
def view():
pass
# 実際の実行順序:
# 1. app.route でルーティング登録
# 2. decorator_a がラップ
# 3. decorator_b がラップ
# リクエスト時: decorator_a → decorator_b → view@app.route は必ず一番上に置くこと。
汎用的なロギングデコレータ
import time
from functools import wraps
def log_execution_time(f):
@wraps(f)
def decorated_function(*args, **kwargs):
start = time.time()
result = f(*args, **kwargs)
elapsed = time.time() - start
app.logger.info(f'{f.__name__} took {elapsed:.3f}s')
return result
return decorated_functionJSON レスポンスを強制するデコレータ
from flask import jsonify
def json_response(f):
@wraps(f)
def decorated_function(*args, **kwargs):
result = f(*args, **kwargs)
if isinstance(result, dict):
return jsonify(result)
return result
return decorated_function
@app.route('/api/user')
@json_response
def get_user():
return {'id': 1, 'name': 'Alice'} # 自動的に jsonify されるレートリミットデコレータ
from flask import request
from functools import wraps
import time
rate_limit_store = {}
def rate_limit(max_requests, window_seconds):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
key = f'{f.__name__}:{request.remote_addr}'
now = time.time()
if key in rate_limit_store:
requests, window_start = rate_limit_store[key]
if now - window_start < window_seconds:
if requests >= max_requests:
return 'Too Many Requests', 429
rate_limit_store[key] = (requests + 1, window_start)
else:
rate_limit_store[key] = (1, now)
else:
rate_limit_store[key] = (1, now)
return f(*args, **kwargs)
return decorated_function
return decorator
@app.route('/api/data')
@rate_limit(10, 60) # 60秒間に10リクエストまで
def get_data():
return {'data': 'value'}カスタムデコレータを活用すれば、ビュー関数をシンプルに保ちながら横断的な関心事を分離できる。












