入門クラス 初心者向け

【Django】クラスベースビューの入門と使い方サンプル(もうすこし詳しく)

以前Qiitaに投稿したのですが、Djangoにはクラスベース汎用ビュー(CBV)というのがあります。

いつか書き直したいなぁと思っていたのですが、新しいサイトを立ち上げたのを機にもう一度書いてみます。

Djangoのバージョンも上がりましたしね。

Djangoでいうところのビューとは

ビューというのはMVCで言うところのコントローラーであり、MTVで言うところのビューです。

Djangoを使った人がある人もない人も、何かの開発に関わればMVCという言葉は聞いたことがあると思います。

MVCはざっくり言うと機能や責任の分離であり、その名称を表しています。
モデル・ビュー・コントローラーですね。
ご存知の方も多いと思います。

DjangoではそのことをMTVと呼んでいます。
モデル・テンプレート・ビューです。

DjangoではMVCでいうビューはテンプレートのことですし、MVCでいうコントローラーはビューと言います。

この三つは実際にDjangoの中核を成しています。
私の講座でもモデルを作り、ビューを作り、そしてテンプレートを作ります。
Djangoにおいてビューの作り方は関数ベースのものとクラスベースのものがあります。

関数のビュー

私はクラスベース汎用ビューを推していますが、機能として単純な方がいい場合は関数ベースの方が良いときもあります。
Djangoのビューはリクエストを受け取ってレスポンスを返せばよいだけなので、書き方として関数とクラスのどちらが良いかはありません。

def index(request):
   return HttpResponse('Hello, World!)

このような一般的なPythonの関数でもビューを作ることができます。

requestという引数はリクエストオブジェクトで、アクセスされたページの情報などが入っています。
そして、HttpResponseはレスポンスオブジェクトを返す関数です。
Djangoではこの二つ、引数としてリクエストオブジェクトを受け取る(一般的にはrequestとする)ことと、レスポンスオブジェクトを返すことだけがビューとして動作する条件となっています。

汎用ビューとはなにか

ビューとはコントローラーの事です。

汎用ビューとはその名の通り、一般的なコントローラーのことです。

例えば、普通にウェブサイトを作成すると

  • データを取り出すモデルが違うので指定する必要がある
  • データを供給するテンプレートのデザインが違うのでテンプレートを指定する必要がある

という処理が必要なだけで、内部的なロジックは変わらないことが多いです。

例えば、

  • ブログのトップページは記事の一覧を表示する
  • ページネーションしているので、一度につき10件
  • ブログのトップページ用のデザインがあるので、テンプレートの指定は必要

  • 商品のトップページは商品の一覧を表示する
  • ページネーションしているので、一度につき10件
  • 商品のトップページ用のデザインがあるので、テンプレートの指定は必要

などです。大体同じようなことをしています。

同じようなことをとは

  • 一覧ページはモデルからデータを取得する
  • ページ分割に応じた件数だけ取得する
  • テンプレートを指定する必要がある

といったことです。

これらの処理をはじめから作っておいて、適切なデフォルト値を設定しておこう、
そして、カスタマイズが必要であればデフォルト値を上書きしてもらおう、
というのが汎用ビューの考え方です。

クラスベースビューではデフォルト値が用意されていて、必要であればデフォルト値を変更する

クラスベース汎用ビューとは

上記の汎用ビューの実装がクラスベースになったものです。
要はMixinとして実装している汎用ビューのことを言います。

もちろん、関数ベースでの汎用ビューもあります(ありました)が、いま使っている人は少ないのではないかと思います。

のっけから長かったですが、これが汎用ビューの存在意義です。

クラスベース汎用ビューの使い方

クラスベース汎用ビューは、先ほど書いてあるとおりMixinです。
従って、自分で作成したクラスで継承して組み込みます。

では、サンプルを書いてみたいと思います。

下準備

ここでは、

django-admin startproject foo

のようにして、プロジェクトはすでに作ってあるものとします。

では、アプリケーションを作っていきましょう。

試しに、ブログ記事のようなものを作ってみたいと思います。

django-admin startapp article

これで、article アプリケーションが作成されました。

つぎの、モデルを作成します。

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()

簡単にするため、フィールドの数は2つにしました。

マイグレーションなどは割愛することにします。

汎用ビューの使い方

続いてviews.pyにクラスベース汎用ビューを使って一覧ページを作ってみたいと思います。

from django.views.generic import ListView

from .models import Article


class ArticleListView(ListView):
    model = Article

基本的にはこれだけでテンプレートにデータが供給されます。

 

上から説明すると

from django.views.generic import ListView

クラスベース汎用ビューは django.views.generic に入っていることが多いです。
別のモジュールからインポートする必要があるものもありますが、8割ここに入っています。

次に、

from .models import Article は自分で作ったモデルですね。
どのモデルからデータを取得するのか指定する必要があるのでここでインポートしています。

次の2行がクラスベース汎用ビューの使い方です。

class ArticleListView(ListView): でクラスを定義しています。
もちろん、クラスベース汎用ビューである ListView を継承しています。

ビューの名前はモデル+汎用ビューの名前であることが多いです。

次に、 model = Article となっています。
ここに指定されたモデルからデータを読み込みます。
デフォルトでは全件読み出してくるので注意が必要です。

テンプレートの指定

さて、ここにテンプレートの指定がありません。
クラスベース汎用ビューではテンプレート名がモデルと汎用ビューの種類で決まります。
今は、

モデル → Article、汎用ビュー → ListView

ですので

article_list.html

という名前のテンプレートが指定されています。通常(モデル名)_(汎用ビューの種類).html という名称が自動で選ばれます。
これはデフォルト値ですので変更することも可能です。

テンプレートのパスですが、実は settings.py の設定に依存しています。

settings.py に自動生成されている TEMPLATE というリストがあります。
要素は辞書になっていて、テンプレートの設定が記述されています。

そこに APP_DIR というキーがあるのですが、この値が True であれば、各アプリケーションの内部の templates というフォルダを探索します。

今回のケースで言えば foo/article/templates フォルダの中身を探索する、という事です。
もしtemplateフォルダが無くても探索しないだけでエラーなどにはなりません。

templates フォルダを作成しましょう。
Pythonモジュールなどではありませんので、ただのフォルダでOKです。
__init__.pyは必要ありません(あっても問題ありません)。

次が重要です。

クラスベース汎用ビューでは、templatesフォルダの中のモデル名のフォルダを探索します。

つまり、今回の場合、templatesフォルダの中の

article/article_list.html を探します。

ですから、まずはfoo/artifle/templates/articleフォルダを作成して、その中にテンプレートファイルであるarticle_list.htmlを作成してください。

urls.pyの設定

urls.py は実はとても重要です。

DjangoはRailsのようにURLを自動で生成しません。

自分でURLを設定する必要があります。
そして、そのURLとビューを紐つけるのがこのurls.pyです。

アクセスできるURLを指定し、そこにアクセスされたらどのビューが反応するかをここで指定します。

クラスベースビューであれば、ここに、as_view()というメソッドを指定します。
そうすれば、あとはクラスベースビューがよしなに処理してくれます。

ListView

from django.views.generic import ListView

from .models import Article

class ArticleListView(LisView):
    model = Article

のように、としてインポートして使います。
model = Article は、リストとして取得したいモデルを指定しておけば、自動で取得してくれます。

しかし、ここでは全件取得してしまいます。ページネーションなどで分割したい場合は、パラメーターを足します。

class ArticleListView(ListView):
    model = Article
    paginated_by = 10

paginated_by は取得するデータを文字通り分割して取得します。
今回は10件にしてみました。
ListViewは分割取得もはじめから組み込まれています。

Viewはページネーションに対応していますが、テンプレートにはページネーションのコードを書かなければなりません。

ページネーションついては別の記事で解説しようと思います。

DetailView

リスト表示があるのであれば個別表示もありますよね。

それがDetailViewです。

from django.views.generic import DetailView

class ArticleDetailView(DetailView):
    model = Article

相変わらずモデルは指定が必要です。

get_context_dataメソッド

クラスベースビューを使っていて、クラスベースビューで管理されている以外のデータを挿入したいというニーズはよくあります。

そんなときは、フックに使える(自分の処理を差し込める)メソッドがあるのでそこにデータを挿入します。
クラスベースビューに定義されているメソッドをオーバーライドすることで、そこに処理を差し込む、という処理をします。

オーバーライドするメソッドですが、大体のクラスベース汎用ビューには get_context_dataメソッドが定義されています。
このメソッドは、テンプレートに渡すデータを取得するメソッドです。

ですので、このメソッドをオーバーライドして、自身の処理を挿入しましょう。

from django.views.generic import ListView

from .models import Article

class ArticleListView(ListView):
    model = Article

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

        context['name'] = 'Dai Takahashi'

        return context

のようにします。

super関数で親クラスの同メソッドを呼び出すと、ListViewがテンプレートに渡す前のデータを返します。
これは辞書ですので、キーを指定して、値を入れておけばテンプレートで呼び出すことができるようになります。

今回の場合、contextという変数で辞書を受け取りました。

続いて、nameというキーで、Dai Takahashiという値を入れています。

最後にreturn contextで変数を返せば良いです。

こうすることにより、テンプレートでは {{ name }} という形で使用できます。

もちろん、他のモデルのデータを呼び出したりすることもできるので、上手に使ってください。

まとめ

こういった表示系の汎用ビューはあまりカスタマイズすることがありません。

しかし、同じページに他のデータを挿入しておきたいケースはよくあると思います。

get_context_dataでほとんど対応できると思います。

全てのページに同じ変数をセットしておきたい場合は、コンテキストプロセッサーというのがあるので、そちらを使うと良いと思います。

-入門クラス, 初心者向け
-,