クラスベース汎用ビューの概説と継承クラス関係について

この記事はDjango Advent Calendar 2019の13日目の記事です。
今回の記事ではDjangoの「汎用ビュー(Class-based Generic View)」の概説と継承クラス関係についてまとめたいと思います。

# 前提
この記事作成時点の最新バージョンであるDjango 3.0の[公式ドキュメント](https://docs.djangoproject.com/ja/3.0/)を参照及び引用しています。

# 汎用ビュー(Class-Based Generic View)とは
まず、Djangoにおける「ビュー」とは、ユーザーリクエストを処理してレスポンスを返すロジックをカプセル化したもの[^1]です。プレゼンテーション層である「テンプレート」に表示するデータを定義したり、リクエストに応じた処理を行います。議論の余地はありますが、MVCフレームワークにおける「コントローラー」が一般的なイメージに近いかもしれません[^2]。

Djangoでは、そのビューを開発する際のロジックを汎化した「汎用ビュー(Class-based Generic View)」が用意されています。クラスベース汎用ビューはその名の通り、クラスベースのビューとなっているため、継承を利用したコードの再利用が可能です[^3]。汎用ビューを活用することでWeb開発における冗長性・ボイラーテンプレートを避け、Djangoの設計思想でもある「Less Code」「Don't Repeat Yourself」を最大限に活かした開発が行えます。

# 汎用ビューの一覧
汎用ビューの一覧と概要をまとめています。なお、重要度は普段の開発における個人的な使用頻度を基準に設定しています。サービスの特性によって重要度は異なるかと思います。

## 基底ビュー
汎用ビューとしての最低限な機能を定義した基底ビュー群です。他の汎用ビューからもここでのクラスが継承されています。

| クラス | 重要度 | 概要 |
| ----- | ----- | --- |
| [View](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/base/#view) | ★★★ | 汎用ビューの基底ビュー |
| [TemplateView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/base/#templateview) | ★★★ | テンプレートをレンダリングするビューの基底ビュー |
| [RedirectView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/base/#redirectview) | ★★☆ | リダイレクトするためのビュー |

## 表示用ビュー
一覧/詳細画面を表示するためのビュー群です。一般的なWebサイトの開発では最頻出なクラスだと思います。

| クラス | 重要度 | 概要 |
| ----- | ----- | --- |
| [DetailView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-display/#detailview) | ★★★ | 詳細画面を表示するためのビュー |
| [ListView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-display/#listview) | ★★★ | 一覧画面を表示するためのビュー |

## 編集用ビュー
編集画面の表示を行うためのビュー群です。一般的なCRUD操作画面で利用できるクラスです。

| クラス | 重要度 | 概要 |
| ----- | ----- | --- |
| [FormView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-editing/#django.views.generic.edit.FormView) | ★★☆ | フォーム画面のビュー |
| [CreateView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-editing/#django.views.generic.edit.CreateView) | ★★☆ | 作成画面のビュー |
| [UpdateView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-editing/#django.views.generic.edit.UpdateView) | ★★☆ | 更新画面のビュー |
| [DeleteView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-editing/#django.views.generic.edit.DeleteView) | ★★☆ | 削除画面のビュー |

## 日付ビュー
ブログのように年別・月別などのような一覧表示を行うためのビュー群です。

| クラス | 重要度 | 概要 |
| ----- | ----- | --- |
| [ArchiveIndexView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-date-based/#archiveindexview) | ★☆☆ | 日付が最新のアイテム一覧をヒョジするためのビュー |
| [YearArchiveView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-date-based/#yeararchiveview) | ★☆☆ | 年別の一覧画面を表示をするためのビュー |
| [MonthArchiveView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-date-based/#montharchiveview) | ★☆☆ | 月別の一覧画面を表示をするためのビュー |
| [WeekArchiveView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-date-based/#weekarchiveview) | ★☆☆ | 週別の一覧画面を表示をするためのビュー |
| [DayArchiveView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-date-based/#dayarchiveview) | ★☆☆ | 日別の一覧画面を表示をするためのビュー |
| [TodayArchiveView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-date-based/#todayarchiveview) | ★☆☆ | 本日日付の一覧画面を表示をするためのビュー |
| [DateDetailView](https://docs.djangoproject.com/ja/3.0/ref/class-based-views/generic-date-based/#datedetailview) | ★☆☆ | 日付ごとの詳細画面を表示をするためのビュー |

# 汎用ビューの構造
汎用ビューはそれ自体が別の汎用ビューを継承していたり、汎用Mixinを多重継承して構成されています。
汎用ビューはその属性と振る舞いを設定することでビューの動作を定義します。なお、ほとんどの属性はその属性に対するアクセッサが定義されているため、アクセッサをオーバーライドすることによって動的に属性の値を変更することも可能です。

## 基底ビュー
### クラス図(`Django.views.generic.base`)
`View`はすべての汎用ビューの基底クラスとなっており、`View`は`request` `kwargs`などの他のクラスから参照される属性を持ちます。
`TemplateView`はテンプレートファイルをレンダリングするための`TemplateResponseMixin`、テンプレートにコンテキスト変数を受け渡す`ContextMixin`を多重継承して構成されています。この2つのMixinは`RedirectView`を除いたすべての汎用ビューで継承されています。
![base.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306546/a05d2a63-657a-461d-0f79-d1aec3ecc175.png)

## 表示用ビュー
### クラス図(`Django.views.generic.list` `Django.views.generic.detail`)
`ListView`と`DetailView`の主な違いは赤枠で囲ったMixinです。これは単にテンプレートに渡すコンテキスト変数がリストか単一オブジェクトなのかによる違いのみで基本的な構造はほぼ同じことがわかります。
![list.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306546/afd495f5-47fa-ca52-b6c1-075b9f21a90e.png)
![detail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306546/7e8eada7-a35e-acf4-afd3-cd23ffcd4fee.png)

## 編集用ビュー
### クラス図(`Django.views.generic.edit`)
継承クラスが多いので、同モジュール内の汎用ビュー、mixinのみ赤枠で囲っています。`DeleteView`を除く汎用ビューはいずれもフォーム生成を行う`FormMixin`と GET時のレンダリングとPOST時のバリデーションを行う`ProcessFormView`を継承しています。残りのビュー、Mixinについては各編集用ビューそれぞれに紐づくCRUD操作に則ったクラスが継承されています。
![edit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306546/a564ca83-49f3-800a-1701-1c1371805024.png)

# 最後に
Djangoを汎用ビューの一般化の概念がつかみにかったり、クラスやMixinとその属性(フィールド)、振る舞い(メソッド)を把握したり、初めてDjangoに触れる人にとっては躓きやすいところだと思います。
クラスベース汎用ビューのHow-to系は良い記事が多くあるので、この記事ではグラフィカルに全体像を把握できるように心がけました。


[^1]: https://docs.djangoproject.com/ja/3.0/#the-view-layer
[^2]: https://docs.djangoproject.com/ja/3.0/faq/general/#django-appears-to-be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-template-how-come-you-don-t-use-the-standard-names
[^3]: https://docs.djangoproject.com/ja/3.0/topics/class-based-views/#class-based-views