記事ページを表示中

Django|クラスベースビューを理解する

記事No.88

更新日時2023年04月13日

Djangoのviewには2つの種類があります。

・ファンクションベースビュー

・クラスベースビュー

両者の違いは、Viewを、関数型プログラミングで記述するか、オブジェクト指向(クラス)で記述するかの違いです。

オブジェクト指向で記述した場合、Djangoが予め用意しているクラス(ジェネリックビュー)を継承する事が出来るので、効率よく開発可能です。

豆知識

オブジェクト指向を理解している場合は、クラスベースビューでviewを記述したほうが、可読性、保守性の面で有利です。

オリジナルのwebアプリを作成する場合でも、そのアプリを構成する要素には、他社のアプリと共通する部分が多々あります。

Djangoは、そういった「全てのWebアプリに共通する要素」を生成するのに便利なクラスを用意しています。

ジェネリックビューの構成について

Djangoが用意しているクラス(ジェネリックビュー)は、「Viewクラス」と「Mixinクラス」を継承しています。

1つのジェネリックビューにつき、Viewクラスは1つしか継承されていません。

一方、Mixinクラスは複数継承する事が出来ます。

ジェネリックビューの種類

Djangoのディレクトリ構成を見ると、以下のディレクトリにジェネリックビューがまとめられています。

django/django/views/generic/

このディレクトリには、以下のモジュールがまとめられています。

  • base.py
  • dates.py
  • detail.py
  • edit.py
  • list.py

それぞれのモジュールに含まれているクラスが、ジェネリックビューになります。

各モジュールのジェネリックビューについて

各モジュールのジェネリックビューは、以下の2種類に分類する事ができます。

  • Viewクラス
  • Mixinクラス

Viewクラスは合計で31個、Mixinクラスは合計で14個あります。

Viewクラスは、継承するルールとして、他のViewクラスは1つだけ継承し、Mixinクラスの場合は複数継承します。

ここからは、各モジュールに含まれているクラスの継承関係について確認します。

Viewクラスの継承関係は以下です。

View*
    TemplateView*
    RedirectView*
    BaseDateListView
        BaseArchiveIndexView
            ArchiveIndexView
        BaseYearArchiveView
            YearArchiveView
        BaseMonthArchiveView
            MonthArchiveView
        BaseWeekArchiveView
            WeekArchiveView
        BaseDayArchiveView
            DayArchiveView
        BaseTodayArchiveView
            TodayArchiveView
        BaseDateDetailView
            DateDetailView
    BaseDetailView
        DetailView*
        BaseDeleteView
            DeleteView*
    ProcessFormView
        BaseFormView
            FormView*
        BaseCreateView
            CreateView*
        BaseUpdateView
            UpdateView*
    BaseListView
        ListView*

*マークのついたViewクラスは、使用頻度の高いViewクラスです。

Djangoに定義されているモジュール一覧

django/views/generic/

Djangoの上記フォルダには「Viewクラスが定義されているモジュール」が含まれています。

Viewクラスの詳細機能を把握したい場合は、モジュールのコードを読むと良いです。

base

baseモジュールには、Viewクラスが3個、Mixinクラスが2個定義されています。

ContextMixin

View

豆知識

全てのViewクラスの元となるクラスです。

TemplateResponseMixin

TemplateView(TemplateResponseMixin, ContextMixin, View)

django.views.generic.base.TemplateView

TemplateViewクラスを使用する事で、HTMLテンプレートをクライアントに返す事ができます。Django側でのレンダリングが無い場合は、以下のように記述する事ができます。

#urls.py
from django.views.generic.base import TemplateView

path('', TemplateView.as_view(template_name='index.html'))#index.htmlをテンプレートとして使用する

Django側で特定の値をHTMLテンプレートに与えてレンダリングする場合、以下のように記述する事が出来ます。

#views.py
class MyTemplateView(TemplateView):
    template_name = 'index.html' #使用するテンプレートを指定
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['time'] = datetime.now() #テンプレートに渡す値を指定
        return context
#urls.py
from .views import MyTemplateView

urlpatterns = [
    path('home/', MyTemplateView.as_view(), name='home'),
]
豆知識

テンプレートを表示するクラスです。ホーム画面などに使います。

RedirectView(View)

豆知識

リダイレクトを行うクラスです。

dates

datesモジュールには、Viewクラスが15個、Mixinクラスが5個定義されています。

YearMixin

MonthMixin

DayMixin

WeekMixin

DateMixin

BaseDateListView(MultipleObjectMixin, DateMixin, View)

BaseArchiveIndexView(BaseDateListView)

ArchiveIndexView(MultipleObjectTempleteResponseMixin, BaseArchiveIndexView)

BaseYearArchiveView(YearMixin, BaseDateListView)

YearArchiveView(MultipleObjectTemplateResponseMixin, BaseYearArchiveView)

BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView)

MonthArchiveView(MultipleObjectTemplateResponseMixin, BaseMonthArchiveView)

BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView)

WeekArchiveView(MultipleObjectTemplateResponseMixin, BaseWeekArchiveView)

BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView)

DayArchiveView(MultipleObjectTemplateResponseMixin, BaseDayArchiveView)

BaseTodayArchiveView(BaseDayArvhiveView)

TodayArchiveView(MultipleObjectTemplateResponseMixin, BaseTodayArchiveView)

BaseDateDetailView(YearMixin, MonthMixin, DayMixin, DateMixin, BaseDetailView)

DateDetailView(SingleObjectTemplateResponseMixin, BaseDateDetailView)

detail

detailモジュールには、Viewクラスが2個、Mixinクラスが2個定義されています。

SingleObjectMixin(ContextMixin)

BaseDetailView(SingleObjectMixin, View)

SingleObjectTemplateResponseMixin(TemplateResponseMixin)

DetailView(SingleObjectTemplateResponseMixin, BaseDetailView)

django.views.generic.detail.DetailView

#views.py
class HogeDetailView(DetailView):
    model = hoge #テンプレートに出力したいモデルを定義する
    template_name = 'hoge.html' #使用するテンプレートを定義する

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context

views.pyでは、出力するモデルと、テンプレートを指定します。

#urls.py
from .views import HogeDetailView

urlpatterns = [
    path('hoge/<int:pk>/', HogeDetailView.as_view(), name='hoge')
]

urls.pyでは、取得したいデータのプライマリキーを指定します。

#hoge.html
{{ object.name }}<br>
{{ object.description }}<br>
{{ object.price }}円<br>

モデルから取り出したデータは、objectに含まれているので、その値をHTMLテンプレートに出力します。

edit

editモジュールには、Viewクラスが9個、Mixinクラスが3個定義されています。

FormMixin(ContextMixin)

ModelFormMixin(FormMixin, SingleObjectMixin)

ProcessFormView(View)

BaseFormView(FormMixin, ProcessFormView)

FormView(TemplateResponseMixin, BaseFormView)

豆知識

Formを表示してデータを送信するクラスです。

BaseCreateView(ModelFormMixin, ProcessFormView)

CreateView(SingleObjectTemplateResponseMixin, BaseCreateView)

豆知識

データベースにデータを挿入するクラスです。

#views.py
class HogeCreateView(CreateView):
    model = Hoge #データを挿入するモデルを指定
    fields = ['name', 'description', 'price'] #モデル内の使用するテーブルを指定
    template_name = 'add_hoge.html' #使用するHTMLテンプレートを指定
    success_url = reverse_lazy('store:list_books') #モデルに書込完了した後に遷移するページを指定。遷移するページの指定は対象モデルに、get_absolute_url=""で指定する事も可能。もしくは、def get_success_url()で定義する事も可能。

    def form_valid(self, form): #フォームからデータを送信する際の処理をカスタマイズできる。
        form.instance.create_at = datetime.now()
        form.instance.update_at = datetime.now()
        return super(BookCreateView, self).form_valid(form)
    
    def get_initial(self, **kwargs): #表示時の初期値を設定する事が可能。
        initial = super(BookCreateView, self).get_initial(**kwargs)
        initial['name'] = 'sample'
        return initial

views.pyには、データを挿入するモデル、使用するHTMLテンプレート、データを挿入した後に遷移するページ、初期値、データを挿入する際の追加処理、などを記述します。

#models.py
class Hoge(HogeModel):
    name = models.CharField(max_length=255)
    description = models.CharField(max_length=1000)
    price = models.IntegerField()

    class Meta:
        #テーブル名をhogeに変更
        db_table = 'hoge'
    
    def get_absolute_url(self): #モデルに書込完了した際に遷移させる関数。遷移先の指定は、views.pyでも指定可能。
        return reverse_lazy('hoge:detail_hoge', kwargs={'pk': self.pk}) #この場合、https://hoge.com/hoge/detail_hoge/pk/に遷移する。

models.pyには、データ挿入後の遷移メソッド(get_absolute_url)を定義します。

遷移先のURLは、ここで指定する事も出来ますが、views.pyで指定した場合は、そちらが優先されます。

#.html
<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <input type="submit" value="保存">
</form>

htmlファイルには、csrfトークンを生成するテンプレートタグと、フォームを生成するテンプレート変数を記述します。

BaseUpdateView(ModelFormMixin, ProcessFormView)

UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView)

豆知識

データを更新するクラスです。

#views.py
class HogeUpdateView(UpdateView):

    template_name = 'hoge.html'
    model = Hoge
    form_class = forms.HogeUpdateForm

    def get_success_url(self):
        return reverse_lazy('hoge:edit_hoge', kwargs={'pk': self.object.id})

views.pyには、使用するHTMLテンプレートと、モデル、生成するフォームを定義します。

get_success_urlメソッドを設定する事で、モデルを更新した後の遷移先を指定する事ができます。

#models.py
class Hoge(BaseModel):
    name = models.CharField(max_length=255)
    description = models.CharField(max_length=1000)
    price = models.IntegerField()

    class Meta:
        #テーブル名をhogeに変更
        db_table = 'hoge'
    
    def get_absolute_url(self):
        return reverse_lazy('store:detail_hoge', kwargs={'pk': self.pk})

models.pyで定義した、get_absolute_urlメソッドで、モデル更新後の遷移先を指定する事ができます。

views.pyでget_success_urlを定義した場合、そちらが遷移先として優先されます。

#.html
<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <input type="submit" value="更新">
</form>

htmlファイルには生成するフォームを定義します。

#urls.py
urlpatterns = [
    path('edit_book/<int:pk>', BookUpdateView.as_view(), name='edit_book')
]

urls.pyでは、更新するデータのpk(プライマリキー)を取得可能にします。

DeletionMixin

DeleteViewCustomDeleteWarning(Warning)

豆知識

データを削除するクラスです。

BaseDeleteView(DeletionMixin, FormMixin, BaseDetailView)

DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView)

#views.py
class HogeView(DeleteView):
    model = Hoge
    template_name = 'hoge.html'
    success_url = reverse_lazy('hoge:list_hoge')

views.pyには、使用するモデル、HTMLテンプレート、データを削除した後に遷移するURLを指定します。

#urls.py
urlpatterns = [
    path('delete_hoge/<int:pk>', HogeDeleteView.as_view(), name='delete_hoge')
]
#.html
<form method="POST">
  {% csrf_token %}
  <p>{{ object.name }}を削除して宜しいでしょうか</p>
  <input type="submit" value="削除する">
</form>

HTMLテンプレートには、テンプレートタグ{% csrf_token %}を記述します。

list

listモジュールには、Viewクラスが2個、Mixinクラスが2個定義されています。

MultipleObjectMixin(ContextMixin)

BaseListView(MultipleObjectMixin, View)

MultipleObjectTemplateResponseMixin(TemplateResponseMixin)

ListView(MultipleObjectTemplateResponseMixin, BaseListView)

django.views.generic.list.ListView

#views.py
class HogeView(ListView):
    model = hoge #使用するモデルを指定
    template_name = 'hoge.html' #使用するテンプレートを指定

    def get_queryset(self): #DBからデータを取り出す方法を指定
        qs = super(HogeView, self).get_queryset()
        if 'name' in self.kwargs:
            qs = qs.filter(name__startswith=self.kwargs['name'])
        qs = qs.order_by('-id')
        return qs
#urls.py
from .views import HogeView

urlpatterns = [
    path('hoge/<name>', HogeView.as_view(), name='hoge'),
]
#hoge.html
<ul>
    {% for object in object_list %}
        <li>名前:{{ object.name }}</li>
        <li>説明:{{ object.description }}</li>
    {% endfor %}
</ul>

DBから取り出したデータは、リスト形式で取り出されるので、それを繰り返し処理でHTMLテンプレートに表示する。

著者情報

名前:スカーレット
2010年からWEBサイトやWEBアプリを作成しています。最初は趣味でブログを書いていましたがSEOを勉強するのが楽しくなり、そのままブロガーとして独立しました。その後、記事を書くだけでは物足りなくなり自分でWEBアプリの作成をスタート。現在はブロガー兼プログラマーとして活動しています。このWEBアプリ(ブロトーク)もDjangoで自作しました。ブロトークはブログとSNSを合体させたようなWEBアプリです。ブログを読んで気づいた事や感想などあれば、気軽にメッセージを送って頂ければと思います。WEB技術を一緒に勉強していけたらと思います。