LaTeX957300 views
雑学1472593 views
高校日本史189857 views
ヒストリア284143 views
中学英語808712 views
Computer365120 views
小学算数1194618 views
中学理科1626207 views
教育148875 views
中学社会667106 views
Help
Tools

English

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) を使うことで、元の関数名やドキュメントが保持される。これがないと endpointdecorated_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_requiredadmin_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_function

JSON レスポンスを強制するデコレータ

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'}

カスタムデコレータを活用すれば、ビュー関数をシンプルに保ちながら横断的な関心事を分離できる。