Class-based viewsについて少し調べてみた

はじめに

略してCBVと書くらしい。Djangoをいじっていると、ViewsをClassで書いたコードに出会って、その便利さに恐れおののいたのでまとめ。

Class-based views

サンプルコードはDjangoの公式から。参考リンクはすべて記事の最後にあります。

CBVは django.views.generic といういかにも継承されそうな名前のモジュールから TemplateViewDetailView などを用途ごとimport して使うことができます。実際のコードを見てみます。

一番シンプルな実装例

urls.py の記述だけでviewを作っちゃうパターンです。

from django.urls import path
from django.views.generic import TemplateView

urlpatterns = [
    path('about/', TemplateView.as_view(template_name="about.html")),
]

as_view ってなんやねん

初見で気になるのはas_view() というクラスメソッド。CBVを使っているとルーティングの時に毎度毎度お世話になるやつですね。これって何なのかなと思って調べるとtell-kさんの記事が出てきたので引用。

DjangoのViewsに必要な要素は以下の3つ。

  • requestオブジェクトを(第一引数として)受け取る
  • callableである
  • responseオブジェクトを返す

as_view() というクラスメソッドはview インスタンスを生成してくれている。つまりClass-based viewsをFunction-based viewsと同じ働きをするようよしなにやってくれているらしい。

ここでは深追いしませんが、tell-kさんの記事にはas_view() のコードまで追っていて面白かったので未チェックの人は是非ご覧くだされ。

CBVを継承してみた

あたり前田のクラッカーですが、views.py 内で継承もできます。

# some_app/views.py
from django.views.generic import TemplateView

class AboutView(TemplateView):
    template_name = "about.html"

これはtemplate_name でテンプレートを指定しているだけ。なのにちゃんと動くという。不思議!(as_view() のおかげですね)

urls.py の方には次の様にルートを追加します。

# urls.py
from django.urls import path
from some_app.views import AboutView

urlpatterns = [
    path('about/', AboutView.as_view()),
]

ただ、GitHubのコードとかを眺めてるとviews内でhoge = HogeView.as_view() という風に変数に代入してルーティングではas_viewを書かないスタイルが散見されました。これはルートにas_view まで書くとダラダラ長ったらしくなるからなのかな?と勝手に解釈してますが、何かパフォーマンス的な問題もあるんだろうか。。。

get の処理をしてみる

GETでアクセスされて「result」と画面に表示したいとき、Function-based Classなら以下のように書けます。

from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # <view logic>
        return HttpResponse('result')

シンプル。CBVに書き直すとこんな風になります。

from django.http import HttpResponse
from django.views import View

class MyView(View):
    def get(self, request):
        # <view logic>
        return HttpResponse('result')

Class内にget() メソッドを追加して処理を定義する。これは as_viewインスタンスを生成したあとにdispatch()メソッドを呼び出してリクエストがGETかPOSTかなど種類を判別してマッチするメソッドにリクエストを受け渡してくれているからだそうです。(もし定義されていなかったらHttpResponsedNotAllowedが発生する)

CBVでフォーム扱う例

CBVでフォームの処理を書くとメソッドごとにGETとPOSTが分けられて見通しがかなりよい。

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View

from .forms import MyForm

class MyFormView(View):
    form_class = MyForm
    initial = {'key': 'value'}
    template_name = 'form_template.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {'form': form})

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            # <process form cleaned data>
            return HttpResponseRedirect('/success/')

        return render(request, self.template_name, {'form': form})

空前絶後超絶怒涛の親Class!!Classを愛しClassに愛されたClass!!TemplateViews・RedirectViews・ListViewsすべてのViewsの生みの親!!そうこの俺こそはああああああ!!Base……Views!!!!ジャアアアアスティイイイス!!!

BaseViewちょっと見てから終わります。

すべてのCBVはこのBaseViewsを継承して生まれる。そうなので、ここを語らずしてCBVを語れないですね。

メソッドのフローチャート

ここまでで若干ネタバレもしてますが。改めて。

  • dispatch(request, *args, **kwargs)

デフォルトの処理はHTTPメソッドを調べて、HTTPメソッドごとにマッチするメソッドを実行する。水先案内人。BaseViewsが受け入れるHTTPメソッドは以下の通り。 'get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'

GETとPOST以外馴染みがないので参考サイトから引っ張ってきた引用を貼ります。

GET:URIのデータを取得する。
POST:URIにリソースを追加する
HEAD:URIのヘッダデータのみを所得する。(データのBodyは必要ないときに使う)
PUT:URIの内容を作成・置換する。
DELETE:URIの内容を削除する。
OPTIONS:URIに対して利用できるメソッドの一覧を取得する。(他の7つのメソッドに対応しないとき使う)
CONNECT:プロキシにトンネリング通信を要求する。トンネリング通信:SSLなどで暗号化したデータを送信する場合、プロキシサーバは中身のデータを判断できない。そこで、CONNECTメソッドで中身のデータのパケットをサーバまで右から左に転送する。
TRACE:クライアントからのリクエストをそのまま返す。
  • http_method_not_allowed(request, *args, **kwargs)

ViewsがサポートしていないHTTPメソッドとともに呼び出されたら実行される。デフォルトの処理ではプレーンテキストで有効なメソッドと一緒にHttpResponseNotAllowedを返す。

  • options()

これは通信オプションを返すためのものらしい。Viewsの有効なHTTPメソッド名リストを含むAllowヘッダとともにレスポンスを返す。

おわりに

CBVの世界はまだまだ奥深そう。Djangoとなると更に……。これからが楽しみです。

参考リンク

Class-based views | Django documentation | Django

Djangoのクラスベースビューのas_viewて何なの? - Qiita

第7回_HTTP verb(HTTP動詞)ってなんですか - 沼田的瀬戸際メモ(仮)