こんにちは!内藤です!
Djangoのクラスベースビューは便利です。
ただ、標準からはずれた動作を実現しようとすると、資料がなくて泣きながらソースコードを追いかける羽目になります。
幸いにも、泣きながら追いかける機会があったので、せっかくなので何回かに分けて、よく使うクラスの流れをまとめてみようと思います。
最終的には、ケーススタディまでまとめられたら、と思っているのですが、果たして・・・?
Index
-
基本的なクラスのシーケンス(この記事)
- View
- RedirectView
- TemplateView
- DetailView
- ListView
-
CRUDクラスのシーケンス(次の記事)
- FormView
- CreateView
- UpdateView
- DeleteView
前提条件
-
対象のDjangoバージョンは、手元にあった 3.2.13 です
-
現場で使える Django の教科書《基礎編》の内容程度が頭に入っている人向け
-
対象とするクラスは、下記
- View
- RedirectView
- TemplateView
- DetailView
- ListView
- FormView
- CreateView
- UpdateView
- DeleteView
Djangoのクラスベースビューの構成
こちらにクラス図を起こしている方がいますが、見るとクラクラします。
クラスビューはすべて django.views.generic.base.View クラスから派生しているのですが、各種 Mixin クラスが入り乱れていて、ソースコード上のどこで何をやっているのかを追いかけるのはなかなか大変です。
各クラスのそれぞれのメソッドがどのタイミングで呼ばれるのかを、ざっくりとまとめてみようと思います。
django.views.generic.base.View
すべての基本になるクラスです。
ソースコードはこちら
urls.py に記載する謎の呪文
メソッドが実装されています。as_view
URLディスパッチャから呼び出されて応答するまでの処理を、簡易的にコールグラフに起こしてみました。
関数名 | 処理内容 |
---|---|
View.as_view | クラスメソッドとして実装されています。 Viewクラスのインスタンスを生成し、インスタンスのメソッドを呼び出します |
setup | 下記のインスタンス変数に値を設定します。 ・self.request ・self.args ・self.kwargs |
dispatch | HTTPメソッドがクラス変数 View.http_method_not_allowed に存在する場合、HTTPメソッドと同名のメソッドを呼び出します。 View.http_method_not_allowed に存在しなかった場合、または、HTTPメソッドと同名のメソッドが定義されていない場合、 http_method_not_allowed メソッドを呼び出します |
オーバーライドの例
dispatchメソッドは、以降のすべての処理の起点になっています。
この関数をオーバーライドすると、すべてのView派生クラスで実行する処理を記述できます。
View内の例外をキャッチ
下記のようなMixinを作成しておき、自作するすべてのViewをこのMixinから派生させると、例外発生時にエラーページに遷移するような動きを実現できます。
from django.http import HttpResponseRedirect
from logging import getLogger
logger = getLogger(__name__)
class CatchExceptionMixin:
error_url = '<エラー時に遷移するURL>'
def dispatch(self, request, *args, **kwargs):
try:
return super().dispatch(request, *args, **kwargs)
except Exception as e
logger.exception(e)
return HttpResponseRedirect(self._get_error_url())
def _get_error_url(self):
return self.error_url
ここでは例外をキャッチしていますが、デバッグ用にパラメータをダンプする等の用途にも使用できます。
django.views.generic.base.RedirectView
別のURLにリダイレクトさせるViewです。
派生元のViewクラスのすべてのメソッドを持っています。
ソースコードはこちら
コールグラフは、dispatchより後ろ側について起こします。
関数名 | 処理内容 |
---|---|
get | GETメソッドでアクセスされた時に呼ばれます。 クラス変数 permanent に応じて、URLディスパッチャに HttpResponseRedirect(302) または HttpResponsePermanentRedirect(301) を返します。 図には載せていませんが、その他のHTTPメソッドでアクセスされた際もこのメソッドを呼び出すようになっており、すべて同様の動作をします。 |
get_redirect_url | リダイレクト先のURLを返すメソッドです。 クラス変数 url またはクラス変数 pattern_name のうち、定義されている方を返します(urlが優先) クラス変数 query_string が True の場合、リダイレクト先にクエリパラメータを引き継ぎます |
表中にも記載しましたが、get以外のメソッドはすべてgetメソッドを呼び出すようになっており、すべてのHTTPメソッドで同一の処理を行うようになっています。
django.views.generic.base.TemplateView
テンプレートをレンダリングして表示するViewです。
派生元のViewクラスのすべてのメソッドを持っています。
ソースコードはこちら
コールグラフは、dispatchより後ろ側について起こします。
関数名 | 処理内容 |
---|---|
ContextMixin.get_context_data | テンプレートエンジンに渡すための context を生成します。 context には、ContextMixin.get_context_data 内部で 'view' というキー名でViewクラスのインスタンスが設定されます |
TemplateResponseMixin.get_template_names | レンダリングするテンプレート名をリストで返します。 クラス変数 template_name の内容を返します。 |
TemplateResponseMixin.render_to_response | テンプレートをレンダリングしてレスポンスを返します。 |
get以外のメソッドは定義されていません。
その他のHTTPメソッドをサポートしたい場合は、post等のメソッドを自力で実装する必要があります。(他のクラスを使用する方が良いと思いますが・・・)
オーバーライドの例
状況によってテンプレートを変更する場合は、get_template_names メソッドをオーバーライドします。
from django.views.generic.base import TemplateView
class MyTemplateView(TemplateView):
anonymous_template = '非ログイン時のテンプレート'
authenticated_template = 'ログイン時のテンプレート'
def get_template_names(self):
if self.request.user.is_authenticated:
return [self.authenticated_template]
else:
return [self.anonymous_template]
django.views.generic.detal.DetailView
あるひとつのデータモデルを表示するためのViewです。
ソースコードはこちら
コールグラフは、dispatchより後ろ側について起こします。
関数名 | 処理内容 |
---|---|
SingleObjectMixin.get_object | 画面に表示するモデルオブジェクトを取得します。 内部で、モデルオブジェクトを取得するのに使用する queryset を取得するために、 get_querysetメソッドを呼び出します。 取得したモデルオブジェクトは self.object に設定されます。 また、get_context_data の中で 'object' というキー名で context にも設定されます。 |
SingleObjectMixin.get_queryset | モデルオブジェクトを取得するための queryset を返します。 |
SingleObjectMixin.get_context_data | テンプレートエンジンに渡すための context を生成します。 context には、'object' というキー名でモデルオブジェクトが設定されます |
ContextMixin.get_context_data | django.views.generic.base.TemplateViewに記述した内容です。 |
SingleObjectTemplateResponseMixin.get_template_names | クラス変数 template_name が設定されていない場合、モデル名からテンプレート名を生成します。 |
TemplateResponseMixin.get_template_names | django.views.generic.base.TemplateViewに記述した内容です。 |
TemplateResponseMixin.render_to_response | django.views.generic.base.TemplateViewに記述した内容です。 |
get以外のメソッドは定義されていません。
その他のHTTPメソッドをサポートしたい場合は、post等のメソッドを自力で実装する必要があります。
django.views.generic.detal.ListView
一種類のモデルの複数のオブジェクトを表示するためのViewです。
ソースコードはこちら
コールグラフは、dispatchより後ろ側について起こします。
関数名 | 処理内容 |
---|---|
BaseListView.get | get_queryset を呼び出し、 self.object_list に格納します。 |
MultipleObjectMixin.get_queryset | 画面に表示するモデルオブジェクトの一覧を、queryset の形で返します。 |
MultipleObjectMixin.get_ordering | get_queryset で取得した queryset の表示順を返します。 クラス変数 ordering を返します。 |
MultipleObjectMixin.get_context_data | get_pagenate_by を呼び出し、ページネーションをするかどうかを判断します。 context に、下記のキー名でデータを設定します。 'paginator' ... ページネーションする場合、pagenator オブジェクト 'page_obj' ... ページネーションする場合、表示対象のページオブジェクト 'is_paginated' ... ページネーションするかどうか 'object_list' ... 表示対象のqueryset |
MultipleObjectMixin.get_paginate_by | クラス変数 paginate_by を返します。 |
MultipleObjectMixin.paginate_queryset | pagenatorオブジェクト や ページオブジェクトを生成します。 ページネーションする場合のみ呼び出されます。 |
ContextMixin.get_context_data | django.views.generic.base.TemplateViewに記述した内容です。 |
MultipleObjectTemplateResponseMixin.get_template_names | クラス変数 template_name が設定されていない場合、モデル名からテンプレート名を生成します。 |
TemplateResponseMixin.get_template_names | django.views.generic.base.TemplateViewに記述した内容です。 |
TemplateResponseMixin.render_to_response | django.views.generic.base.TemplateViewに記述した内容です。 |
get以外のメソッドは定義されていません。
その他のHTTPメソッドをサポートしたい場合は、post等のメソッドを自力で実装する必要があります。