はじめに
略してCBVと書くらしい。Djangoをいじっていると、ViewsをClassで書いたコードに出会って、その便利さに恐れおののいたのでまとめ。
Class-based views
サンプルコードはDjangoの公式から。参考リンクはすべて記事の最後にあります。
CBVは django.views.generic
といういかにも継承されそうな名前のモジュールから TemplateView
や DetailView
などを用途ごと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