この投稿は 「Django Advent Calendar 2020 – Qiita」 12日目の記事です。
初めまして、SUNABACOの いとむら です。
この記事は、Djangoで初めて「Class-based views
」を使って開発をする人の手助けになるといいなと思って書きました。
Class-based views とは
Class-based views
は「汎用クラスビュー」または「汎用ビュー」とも言います。公式ドキュメントには以下のように説明されています。
ビューとは、リクエストを受け取りレスポンスを返す、呼び出し可能なオブジェクトです。(略)。ビューを構造化し、継承とミックスインを利用してコードを再利用することを可能にします。
https://docs.djangoproject.com/ja/2.2/topics/class-based-views/
私の理解だと、一般的に使われるであろう(汎用性のある)処理をつくっておき(構造化)し、それぞれ使うときに継承、ミックスインの利用でビューを作り上げいくということです。
そもそもビューとは
DjangoのMVTでいうところの「V」に当たる部分です。本記事では触れませんが、ビューを書く方法としては「function-based views
」もあります。公式ドキュメントでは Class-based views
より先に function-based views
について触れられています。 https://docs.djangoproject.com/ja/3.1/topics/
ビューは前提として以下の3つの条件を満たさないといけません。
request
を受け取れることresponse
を返すこと- 呼び出し可能であること
function-based-views
であろうと Class-based views
であろうと以上3つの条件を満たす必要があります。なので両者とも最終的にやっていることは同じになります。
ビューの例
リソース作成の処理を function-based views
と Class-based views
で見比べてみます。(CRUDでいうところのCreate)
function-based views
from django.shortcuts import render
from items.forms import ItemForm
def item_create(request):
"""商品の新規作成をする"""
if request.method == 'GET':
form = ItemForm()
return render(
request,
'stores/item_form.html',
dict(form=form)
)
elif request.method == 'POST':
form = ItemForm(request.POST)
if form.is_valid():
form.save()
return redirect(form.instance.get_absolute_url())
else:
return render(
request,
'stores/item_form.html',
dict(form=form)
)
Class-based views
from django.views import generic
from items.forms import ItemForm
from items.models import Item
class ItemCreateView(generic.CreateView):
"""商品の新規作成をする"""
model = Item
form_class = ItemForm
def get_success_url(self):
return self.object.get_absolute_url()
item_create = ItemCreateView.as_view()
フォームクラスについては触れません。また、 Class-based views
のコードの最後にある as_view()
については過去のDjangoアドベントカレンダーでtell-kさんが詳しく書かれていますのでそちらをご確認ください。 Djangoのクラスベースビューのas_viewて何なの?
上記に2パターンのビューの書き方を例に出しました。私が Class-based-views
の書き方を初めて見たとき、「よくわからない。。。」が感想でした。 (この記事を読んでいる方は function-based views
でビューを一度でも書いた事あると思ってます笑)
Class-based views
でビューを定義したとき、モデルの指定やフォームクラスの指定については、「まー、なんとなくわかる」。でも、テンプレートへ渡す変数は?そもそもテンプレート名の指定は?as_view()って?みたいに謎だらけでした。function-based views
でビューを定義した場合は、上から下に処理を追っていくだけなので何をやっているかがわかりやすい。しかし、Class-based views
でビューを定義した場合は、処理を追えない。これは、汎用クラスビューを継承して定義したので、見えるところには処理は書いていないからです。なので、理解するためには継承した CreateView
を調べないといけません。
そんなときに超便利な Classy Class-Based Views. を使うわけです!やっと本題。
Class-based views
については akiyokoさんの 現場で使える Django の教科書《基礎編》【紙の本】 で学べますので是非御覧ください。僕は基礎編も発展編も紙とKindleの両方を持っています。
Classy Class-Based Views.

このサイトは、Djangoで開発する際にChromeのタブに常駐させています。
Classy Class-Based Views.
TOPページに、汎用ビューがまとめられています。デフォルトで表示されているだけで24個あります。「Show more」を押せばミックスイン含めさらに表示されます。

今回は、前の例で CreateView
をみていきます。
https://ccbv.co.uk/projects/Django/3.0/django.views.generic.edit/CreateView/


見方は図に示した通りです。一番の有用と思うのが「Hierarchy diagram」ボタンを押したときに表示される継承関係を示した図です。

汎用クラスビューはそれぞれで、上書きできる属性や関数が異なっています。また、公式ドキュメントではそれらが整理されていなく、GitHubでDjangoのコードを読みに行くしか方法はない。。。のですが、この「Classy Class-Based Views.」には、属性や関数、継承関係など見やすくまとめられています。またメソッドの処理もまとめて書かれているのでGitHubまでコードを見に行かなくても済みます。
CreateViewのGETリクエスト
CreateView
のGETリクエストでは下記の順番で処理されます。super()
で親クラスのメソッドを呼び出している。
def get(self, request, *args, **kwargs):
self.object = None
return super().get(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
return self.render_to_response(self.get_context_data())
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
if 'form' not in kwargs:
kwargs['form'] = self.get_form()
return super().get_context_data(**kwargs)
def get_context_data(self, **kwargs):
"""Insert the single object into the context dict."""
context = {}
if self.object:
context['object'] = self.object
context_object_name = self.get_context_object_name(self.object)
if context_object_name:
context[context_object_name] = self.object
context.update(kwargs)
return super().get_context_data(**context)
def get_context_data(self, **kwargs):
kwargs.setdefault('view', self)
if self.extra_context is not None:
kwargs.update(self.extra_context)
return kwargs
def render_to_response(self, context, **response_kwargs):
"""
Return a response, using the `response_class` for this view, with a
template rendered with the given context.
Pass response_kwargs to the constructor of the response class.
"""
response_kwargs.setdefault('content_type', self.content_type)
return self.response_class(
request=self.request,
template=self.get_template_names(),
context=context,
using=self.template_engine,
**response_kwargs
)
これも「Classy Class-Based Views.」を見ながらだと簡単に追うことができます。ぜひご自身で、処理を追ってみてください。
おわりに
Class-based views
でビューを作ろうと思ったときに、概要を掴むために「Classy Class-Based Views.」が有用だということを伝えました。まず、これで全体をつかみそのあとに、Djangoのコード、公式ドキュメントを見るとさらに理解が進むと思います。
この記事が、Class-based views
を使った開発をする方の手助けとなれば嬉しく思います。
【宣伝】デザインコーススクール生募集
デザイン思考、情報アーキテクチャ、UI、UX、タイポグラフィ、色彩論、Adobe系ツールなど、「デザインは機能である」ことを学ぶ4週間のスクールを開催します。また、今回は弊社代表が大手企業コンサルでも実施している「ユーザービリティエンジニアリング」について特別講義も入っています!
申込締切 2021年1月8日(金) 面接日 1月9日(土) 受講期間 1月11日(月)~2月5日(金) 土曜・日曜を除く20日間 講義時間 昼コース 13:00 ~ 16:00, 夜コース 19:00 ~ 22:00 新規受講 ¥70,000(税別) 継続受講 デザインコースの卒業生は継続料金¥20,000(税別)で受講可能