クラスベースのView
クラスベースのView
前章で扱ったdefで書く関数ベースのViewとは違い、classで記述する方法を、クラスベースビュー または クラスベース汎用ビュー と呼びます。
汎用とは英語でGenericと言い、Djangoのdjango.generic.views
から様々な汎用ビューをインポートして、classでViewを作成していきます。
Djangoは公式ドキュメントでもクラスベースビューを推奨しています。
理由は、同じコードを書かない(DRY)というDjangoの思想に基づいているもので、
継承とミックスインを用いれば、同じコードを書くケースを減らせるというもの。
現在、defの関数ベースでViewを書いている場合は、今後少しづつクラスベースのViewを導入していけると良いでしょう。
関数ベースとクラスベースの比較
関数ベースで以下のようなコードがあるとします。
from django.http import HttpResponse
def index(request):
if request.method == 'GET':
print('GETリクエスト')
return HttpResponse(200)
if request.method == 'POST':
print('POSTリクエスト')
return HttpResponse(200)
クラスベースで書くと以下のように記述できます。
from django.http import HttpResponse
from django.views import View
class IndexView(View):
def get(self, request):
print('GETリクエスト')
return HttpResponse(200)
def post(self, request):
print('POSTリクエスト')
return HttpResponse(200)
コードを比較すればわかると思いますが、クラスベースで書く方が圧倒的に処理がわかりやすいです。
さらにクラスで書くことで、クラス内関数や変数なども記述できるので、使用するメソッドなどをクラスごとに分けることも可能になってきます。
クラスベースビューは、django.views
から View
をインポートしてクラスの引数に渡すことで使用できますが、View
以外にもクラスベース汎用ビューと呼ばれるものがあり、CRUDと呼ばれるデータベースの作成・取得・更新・削除などの処理に特化したビューが存在します。
それらを適切に用いることで、Djangoのプロジェクト開発をスムーズにかつスピーディーに進めることができます。
クラスベース汎用ビュー
今後、クラスベース汎用ビューもまとめてクラスビューと呼びますが、
まずはどういったものがあるのか主要なものをまとめてみました。
クラスベースビュー一覧
- TemplateView
基本的なページビュー - CreateView
データの作成のためのビュー - UpdateView
データの更新のためのビュー - DeleteView
データの削除のためのビュー - ListView
複数のデータを取得するビュー - DetailView
一つのデータを取得するビュー
まずはTemplateViewから見ていきましょう。
TemplateView
TemplateViewは最も基本的なクラスビューで、たった数行でページのバックエンドを組むことができます。
views.py
from django.views.generic import TemplateView
class IndexView(TemplateView):
template_name = 'index.html'
index = IndexView.as_view()
これでindex
をurls.pyに記述するだけで、index.html
の内容をページに反映させることができます。
全てのクラスビューには、オーバーライドできるデフォルトの関数があります。
例えば、上記のコードでデータを追加でフロントエンドに投げたい場合、get_context_data
という関数を追加することでそれを実現できます。
views.py
from django.views.generic import TemplateView
class IndexView(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['hoge'] = 'Hello world!'
return context
index = IndexView.as_view()
index.html
で {{ hoge }}
と入力してみてください。
Hello world!とページに表示されるはずです。
基本的に全てのDjangoのページには context という辞書型のデータがバックエンドから投げられています。
get_context_data
は、その辞書の中に好きなデータを投入することができます。
他にも以下のような関数が主にオーバーライドされることが多いです。
- get
- post
- dispatch
getやpostは既に前述したように、GETやPOSTなどのリクエストに応じて、処理を加えることができます。
dispatchは、ページにリクエストがきた際にそれをGETやPOSTに振り分ける処理のことです。
つまり、dispatchは、getやpostの処理が呼ばれる前に呼ばれる処理です。
これをオーバーライドすることで真っ先に必要な処理を行うことができます。
views.py
from django.views.generic import TemplateView
from django.http import HttpResponse
class IndexView(TemplateView):
template_name = 'index.html'
def dispatch(self, request, *args, **kwargs):
print('DISPATCH')
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['hoge'] = 'Hello world!'
return context
def get(self, request, *args, **kwargs):
print('GET')
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
print('POST')
return HttpResponse(200)
index = IndexView.as_view()
出力(GETリクエスト)
DISPATCH
GET
CreateView
TemplateViewを使えば、ほぼ全てのページをクラスベースで開発していくことができます。
ただし、formを使ったデータの保存処理を全てpostに記述していくのは面倒です。
データの作成にはCreateViewを使うことでコードの記述量を減らすことが可能です。
models.pyにArticleというclassを記述しているとしましょう。
views.py
from django.views.generic import CreateView
from django.urls import reverse_lazy
from .models import Article
class ArticleCreateView(CreateView):
template_name = 'article/create.html'
model = Article
fields = ['title', 'body']
success_url = reverse_lazy('sample_app:index')
article_create = ArticleCreateView.as_view()
template_name
はTemplateViewと同じです。
model
には、該当するデータのクラス名を記述します。(models.pyからインポートします。)
fields
には、リストかタプルで扱うフィールドを指定します。
success_url
には保存処理が成功した後の遷移先のURLを記述します。
なおarticle/create.html
にて{{ form }}
と記述すると自動でフォームが生成されます。
POSTのフォームで囲い、送信ボタンを設置することで保存処理の一連の流れを実装することができます。
初めてみた方はこれだけ?と驚かれるかもしれませんが、本当にこれだけでデータが保存されます。
実際には、form_valid
という関数が全ての処理を行ってくれています。
form_valid
もget
やpost
などのようにオーバーライドすることができます。
views.py
from django.views.generic import CreateView
from django.urls import reverse_lazy
from .models import Article
class ArticleCreateView(CreateView):
template_name = 'article/create.html'
model = Article
fields = ['title', 'body']
success_url = reverse_lazy('sample_app:index')
def form_valid(self, form):
article = form.save(commit=False)
article.creator = self.request.user.username
article.save()
return super().form_valid(form)
article_create = ArticleCreateView.as_view()
ここでは、保存処理が走る前に、creatorフィールドにユーザーネームを入れて保存処理を入れました。
form_valid
は、CreateView, UpdateView, FormViewにて使用することができます。
UpdateView
UpdateViewはCreateViewとほとんど同じです。
違いはデータの作成ではなく、既存のデータの更新ということ。
views.py
from django.views.generic import UpdateView
from django.urls import reverse_lazy
from .models import Article
class ArticleUpdateView(UpdateView):
template_name = 'article/update.html'
model = Article
fields = ['title', 'body']
success_url = reverse_lazy('sample_app:index')
article_update = ArticleUpdateView.as_view()
使い方はほとんど同じです。
update.html
にて、{{ form }}
と入力するとCreateViewと同じようにフォームが自動生成されますが、違うのはそのフォームのinputの中に既存のデータが入っています。
また保存処理が走った際もデータが新しく作られることはなく、既存のデータが更新されます。
そのためにURLにidやそのほかのフィールドを付け加える必要があります。
urls.py
path('article/<int:pk>/update', views.article_update, name='article_update),
上記のpkとはPrimary Keyでidのことです。intはIntegerでデータの型を指定しています。
UpdateViewでは、このidから内部的に既存のデータを取得してきて、更新処理を行います。
DeleteView
あまり使われることは少ないかもしれませんが、DeleteViewというものも存在します。
これはデータの削除に使われるクラスビューです。
views.py
from django.views.generic import DeleteView
from django.urls import reverse_lazy
from .models import Article
class ArticleDeleteView(DeleteView):
template_name = 'article/delete.html'
model = Article
success_url = reverse_lazy('sample_app:index')
# delete処理をオーバーライドできる
def delete(self, request, *args, **kwargs):
print(self.object.title)
return super().delete(request, *args, **kwargs)
article_delete = ArticleDeleteView.as_view()
UpdateViewやCreateViewと違い、フォームにフィールドはないのでfields
は指定しませんが、リクエストを扱うので、POSTフォームをフロントで作成する必要があります。
POSTリクエストが送られると自動で該当するデータを削除します。
そのため、UpdateViewと同じようにURLに<int:pk>
のようにidなどの一意なデータを加える必要があります。
またdef delete()
をオーバーライドすることで削除する際の処理をカスタマイズすることも可能です。
ListView
ListViewは複数のデータを取得するクラスビューです。
Djangoでは、複数のデータのセットのことをクエリセットといいます。
クエリセットはリストのようなデータの型になっており、データが存在しなくてもエラーにならないのが特徴です。
ListViewではこのクエリセットを取得します。
views.py
from django.views.generic import ListView
from .models import Article
class ArticleListView(ListView):
template_name = 'article/list.html'
model = Article
context_object_name = 'articles'
paginate_by = 10
article_list = ArticleListView.as_view()
デフォルトでは、article/list.html
で {{ object_list }}
でこのデータを表示することができます。
上記のコードでは、context_object_name
をarticles
に指定しています。
こうすることで、{{ object_list }}
の代わりに {{ articles }}
でデータを表示することができます。
ListViewの特徴は簡単にページネーションを実装できることにあります。
上記のコードでpaginate_by
に10を指定しています。
これにより、取得するデータを10に制限しています。
DjangoのTemplate機能を使うことで、ページネーションも簡単に実装することができます。
逆にこれをしない場合、データが10000などになってくると10000のデータを一度で取得することになるので、ページの読み込み速度が著しく遅くなってしまいます。
本来は面倒くさくなるページネーション実装ですが、ListViewを使うことですごく簡単に組み込むことができます。
Bootstrapを併用したページネーションコードの一例
article/list.html
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a
class="page-link"
href="?page={{ page_obj.previous_page_number }}"
aria-label="Previous"
>
<span aria-hidden="true">«</span>
<span class="sr-only">Previous</span>
</a>
</li>
{% endif %}
{% for num in paginator.page_range %}
{% if num <= page_obj.number|add:5 and num >= page_obj.number|add:-5 %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">
{{ num }}
<span class="sr-only">(current)</span>
</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a
class="page-link"
href="?page={{ page_obj.next_page_number }}"
aria-label="Next"
>
<span aria-hidden="true">»</span>
<span class="sr-only">Next</span>
</a>
</li>
{% endif %}
</ul>
</nav>
TemplateViewでも述べましたが、このListViewでも get
やpost
、get_context_data
などを扱うことができます。
追加で処理が必要な場合これらを組み込んでいくと良いでしょう。
DetailView
今回のクラスビューの最後に扱うのがDetailViewです。
DetailViewでは、クエリセットを扱うListViewとは違い、単体のデータ: オブジェクト
を扱うためのビューです。
オブジェクトを扱うビューは UpdateView
やDeleteView
で触れました。
つまり、DetailViewでも、urls.pyに<int:pk>
などの一意なデータを記述する必要があります。
つまりListViewが記事の一覧ページにあたり、DetailViewが記事を読むページになります。
views.py
from django.views.generic import DetailView
from .models import Article
class ArticleDetailView(DetailView):
template_name = 'article/detail.html'
model = Article
context_object_name = 'article'
slug_field = 'slug'
slug_url_kwarg = 'slug'
article_detail = ArticleDetailView.as_view()
新しくslug_field
とslug_url_kwarg
が追加されています。
例えば、URLでidではなく、slug(URLスラッグ)を使いたい場合、このように記述します。
slug_field
で該当するフィールド名を、
slug_url_kwarg
でurls.pyに記述する名前を記入しています。
これによりURLが
/article/1
ではなく
article/first-post
などのように表示されます。
他の変数は全て既に扱ったとおりです。
ここでは、context_object_name
でデフォルトでは {{ object }}
となるデータ名を {{ article }}
に変更しています。
さいごに
Djangoのクラスビューは、デフォルトで記述されているものをオーバーライドして使用しています。
つまり仮想環境にインストールしているDjangoのコードを全てみていけばその内容が全て書かれています。
今回扱ったビュー以外にも日付を扱うものであったり、フォームを扱うものであったり様々なビューが存在します。
今回扱ったもの以外は、必要になった時に調べて使っていければ良いでしょう。
CRUD処理を適切に使い分けることで。Djangoの開発はよりスムーズになります。
さらに別の人がコードを見た時に、簡単に処理を見分けることができコード全体の可読性がとても高くなります。
少しづつ導入できると良いでしょう。
Just Python フリープラン
ジャスパイなら教材は全て無料!