デコレータの表示—フラスコのドキュメント

提供:Dev Guides
< FlaskFlask/docs/1.0.x/patterns/viewdecorators
移動先:案内検索

デコレータを表示する

Pythonには、関数デコレータと呼ばれる非常に興味深い機能があります。 これにより、Webアプリケーションにとって非常に優れた機能がいくつか可能になります。 Flaskの各ビューは関数であるため、デコレータを使用して1つ以上の関数に追加の機能を挿入できます。 route()デコレータは、おそらくすでに使用しているものです。 ただし、独自のデコレータを実装するためのユースケースがあります。 たとえば、ログインしているユーザーだけが使用する必要があるビューがあるとします。 ユーザーがサイトにアクセスしてログインしていない場合は、ログインページにリダイレクトする必要があります。 これは、デコレータが優れたソリューションであるユースケースの良い例です。

ログインが必要なデコレータ

それでは、そのようなデコレータを実装しましょう。 デコレータは、別の関数をラップして置き換える関数です。 元の関数が置き換えられるため、元の関数の情報を新しい関数にコピーすることを忘れないでください。 functools.wraps()を使用してこれを処理します。

この例では、ログインページの名前が'login'であり、現在のユーザーがg.userに保存され、誰もログインしていない場合はNoneであると想定しています。

from functools import wraps
from flask import g, request, redirect, url_for

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if g.user is None:
            return redirect(url_for('login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function

デコレータを使用するには、ビュー関数の最も内側のデコレータとして適用します。 さらにデコレータを適用するときは、route()デコレータが最も外側にあることを常に覚えておいてください。

@app.route('/secret_page')
@login_required
def secret_page():
    pass

ノート

next値は、ログインページのGET要求の後、request.argsに存在します。 ログインフォームからPOSTリクエストを送信するときに、それを渡す必要があります。 非表示の入力タグを使用してこれを実行し、ユーザーのログイン時にrequest.formから取得できます。

<input type="hidden" value="{{ request.args.get('next', '') }}"/>

キャッシングデコレータ

コストのかかる計算を行うビュー関数があり、そのために生成された結果を一定時間キャッシュしたいとします。 デコレータはそのためにいいでしょう。 キャッシュに記載されているようなキャッシュを設定していることを前提としています。

キャッシュ関数の例を次に示します。 特定のプレフィックス(実際にはフォーマット文字列)とリクエストの現在のパスからキャッシュキーを生成します。 最初にデコレータを作成し、次に関数をデコレートする関数を使用していることに注意してください。 ひどいですね。 残念ながら、それはもう少し複雑ですが、コードはそれでも読みやすいはずです。

装飾された機能は次のように機能します

  1. 現在のパスに基づいて現在のリクエストの一意のキャッシュキーを取得します。
  2. キャッシュからそのキーの値を取得します。 キャッシュが何かを返した場合、その値を返します。
  3. それ以外の場合は、元の関数が呼び出され、指定されたタイムアウト(デフォルトでは5分)の間、戻り値がキャッシュに格納されます。

ここにコードがあります:

from functools import wraps
from flask import request

def cached(timeout=5 * 60, key='view/%s'):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            cache_key = key % request.path
            rv = cache.get(cache_key)
            if rv is not None:
                return rv
            rv = f(*args, **kwargs)
            cache.set(cache_key, rv, timeout=timeout)
            return rv
        return decorated_function
    return decorator

これは、インスタンス化された cache オブジェクトが使用可能であることを前提としていることに注意してください。詳細については Caching を参照してください。


テンプレートデコレータ

しばらく前にTurboGearsの人たちによって発明された一般的なパターンは、テンプレートデコレータです。 そのデコレータの考え方は、view関数からテンプレートに渡された値を含む辞書を返すことであり、テンプレートは自動的にレンダリングされます。 それで、次の3つの例はまったく同じことをします:

@app.route('/')
def index():
    return render_template('index.html', value=42)

@app.route('/')
@templated('index.html')
def index():
    return dict(value=42)

@app.route('/')
@templated()
def index():
    return dict(value=42)

ご覧のとおり、テンプレート名が指定されていない場合は、ドットがスラッシュ+ '.html'に変換されたURLマップのエンドポイントが使用されます。 それ以外の場合は、指定されたテンプレート名が使用されます。 装飾された関数が戻ると、返された辞書がテンプレートレンダリング関数に渡されます。 Noneが返される場合、空の辞書が想定されます。辞書以外のものが返される場合、関数から変更せずにそれを返します。 そうすれば、リダイレクト関数を使用したり、単純な文字列を返したりすることができます。

そのデコレータのコードは次のとおりです。

from functools import wraps
from flask import request, render_template

def templated(template=None):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            template_name = template
            if template_name is None:
                template_name = request.endpoint \
                    .replace('.', '/') + '.html'
            ctx = f(*args, **kwargs)
            if ctx is None:
                ctx = {}
            elif not isinstance(ctx, dict):
                return ctx
            return render_template(template_name, **ctx)
        return decorated_function
    return decorator

エンドポイントデコレータ

柔軟性を高めるためにwerkzeugルーティングシステムを使用する場合は、Ruleで定義されているエンドポイントをビュー関数にマップする必要があります。 これは、このデコレータで可能です。 例えば:

from flask import Flask
from werkzeug.routing import Rule

app = Flask(__name__)
app.url_map.add(Rule('/', endpoint='index'))

@app.endpoint('index')
def my_index():
    return "Hello world"