プラグ可能なビュー—フラスコのドキュメント

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

プラグ可能なビュー

バージョン0.7の新機能。


Flask 0.7では、関数ではなくクラスに基づくDjangoの汎用ビューに触発されたプラグ可能なビューが導入されています。 主な目的は、実装の一部を置き換えることができ、この方法でカスタマイズ可能なプラグ可能なビューを使用できるようにすることです。

基本的な原則

データベースからオブジェクトのリストをロードし、テンプレートにレンダリングする関数があるとします。

@app.route('/users/')
def show_users(page):
    users = User.query.all()
    return render_template('users.html', users=users)

これは単純で柔軟性がありますが、他のモデルやテンプレートにも適応できる一般的な方法でこのビューを提供する場合は、より柔軟性が必要になる場合があります。 ここで、プラグイン可能なクラスベースのビューが配置されます。 これをクラスベースのビューに変換する最初のステップとして、次のようにします。

from flask.views import View

class ShowUsers(View):

    def dispatch_request(self):
        users = User.query.all()
        return render_template('users.html', objects=users)

app.add_url_rule('/users/', view_func=ShowUsers.as_view('show_users'))

ご覧のとおり、flask.views.Viewのサブクラスを作成し、dispatch_request()を実装する必要があります。 次に、as_view()クラスメソッドを使用して、そのクラスを実際のビュー関数に変換する必要があります。 その関数に渡す文字列は、ビューが持つエンドポイントの名前です。 しかし、これ自体は役に立たないので、コードを少しリファクタリングしましょう。

from flask.views import View

class ListView(View):

    def get_template_name(self):
        raise NotImplementedError()

    def render_template(self, context):
        return render_template(self.get_template_name(), **context)

    def dispatch_request(self):
        context = {'objects': self.get_objects()}
        return self.render_template(context)

class UserView(ListView):

    def get_template_name(self):
        return 'users.html'

    def get_objects(self):
        return User.query.all()

もちろん、これはそのような小さな例にはそれほど役立ちませんが、基本的な原則を説明するには十分です。 クラスベースのビューがある場合、selfが何を指しているのかという質問が出てきます。 これが機能する方法は、リクエストがディスパッチされるたびにクラスの新しいインスタンスが作成され、dispatch_request()メソッドがURLルールのパラメーターを使用して呼び出されることです。 クラス自体は、as_view()関数に渡されたパラメーターを使用してインスタンス化されます。 たとえば、次のようなクラスを作成できます。

class RenderTemplateView(View):
    def __init__(self, template_name):
        self.template_name = template_name
    def dispatch_request(self):
        return render_template(self.template_name)

そして、次のように登録できます。

app.add_url_rule('/about', view_func=RenderTemplateView.as_view(
    'about_page', template_name='about.html'))

メソッドのヒント

プラグ可能なビューは、route()またはそれ以上のadd_url_rule()を使用して、通常の関数のようにアプリケーションにアタッチされます。 ただし、これを添付するときに、ビューがサポートするHTTPメソッドの名前を指定する必要があることも意味します。 その情報をクラスに移動するために、次の情報を持つmethods属性を指定できます。

class MyView(View):
    methods = ['GET', 'POST']

    def dispatch_request(self):
        if request.method == 'POST':
            ...
        ...

app.add_url_rule('/myview', view_func=MyView.as_view('myview'))

メソッドベースのディスパッチ

RESTful APIの場合、HTTPメソッドごとに異なる関数を実行すると特に役立ちます。 flask.views.MethodViewを使用すると、簡単にそれを行うことができます。 各HTTPメソッドは、同じ名前の関数にマップされます(小文字のみ)。

from flask.views import MethodView

class UserAPI(MethodView):

    def get(self):
        users = User.query.all()
        ...

    def post(self):
        user = User.from_form_data(request.form)
        ...

app.add_url_rule('/users/', view_func=UserAPI.as_view('users'))

そうすれば、methods属性を指定する必要もありません。 クラスで定義されたメソッドに基づいて自動的に設定されます。


ビューを飾る

ビュークラス自体はルーティングシステムに追加されるビュー関数ではないため、クラス自体を装飾することはあまり意味がありません。 代わりに、as_view()の戻り値を手作業で装飾する必要があります。

def user_required(f):
    """Checks whether user is logged in or raises error 401."""
    def decorator(*args, **kwargs):
        if not g.user:
            abort(401)
        return f(*args, **kwargs)
    return decorator

view = user_required(UserAPI.as_view('users'))
app.add_url_rule('/users/', view_func=view)

Flask 0.8以降、クラス宣言に適用するデコレータのリストを指定できる別の方法もあります。

class UserAPI(MethodView):
    decorators = [user_required]

呼び出し元の観点からの暗黙の自己のため、ビューの個々のメソッドで通常のビューデコレータを使用することはできませんが、これを覚えておいてください。


APIのメソッドビュー

Web APIはHTTP動詞と非常に緊密に連携していることが多いため、MethodViewに基づいてそのようなAPIを実装することは非常に理にかなっています。 そうは言っても、APIには、ほとんどの場合同じメソッドビューに移動するさまざまなURLルールが必要であることに気付くでしょう。 たとえば、Web上でユーザーオブジェクトを公開しているとします。

URL メソッド 説明
/users/ GET すべてのユーザーのリストを提供します
/users/ POST 新しいユーザーを作成します
/users/<id> GET シングルユーザーを表示します
/users/<id> PUT シングルユーザーを更新します
/users/<id> DELETE 1人のユーザーを削除します

では、MethodViewでそれをどのように行うのでしょうか? 秘訣は、同じビューに複数のルールを提供できるという事実を利用することです。

今のところ、ビューは次のようになっていると仮定しましょう。

class UserAPI(MethodView):

    def get(self, user_id):
        if user_id is None:
            # return a list of users
            pass
        else:
            # expose a single user
            pass

    def post(self):
        # create a new user
        pass

    def delete(self, user_id):
        # delete a single user
        pass

    def put(self, user_id):
        # update a single user
        pass

では、これをルーティングシステムにどのように接続するのでしょうか。 2つのルールを追加し、それぞれのメソッドに明示的に言及することにより、次のようになります。

user_view = UserAPI.as_view('user_api')
app.add_url_rule('/users/', defaults={'user_id': None},
                 view_func=user_view, methods=['GET',])
app.add_url_rule('/users/', view_func=user_view, methods=['POST',])
app.add_url_rule('/users/<int:user_id>', view_func=user_view,
                 methods=['GET', 'PUT', 'DELETE'])

似たようなAPIがたくさんある場合は、その登録コードをリファクタリングできます。

def register_api(view, endpoint, url, pk='id', pk_type='int'):
    view_func = view.as_view(endpoint)
    app.add_url_rule(url, defaults={pk: None},
                     view_func=view_func, methods=['GET',])
    app.add_url_rule(url, view_func=view_func, methods=['POST',])
    app.add_url_rule('%s<%s:%s>' % (url, pk_type, pk), view_func=view_func,
                     methods=['GET', 'PUT', 'DELETE'])

register_api(UserAPI, 'user_api', '/users/', pk='user_id')