はじめに
Djangoは「クラスベース汎用ビュー」というクラスを使ったビューが用意されていて、少ないコードで豊富な機能を実装することができます。その一方でコードが内部に隠れてしまうので、仕組みが分かりにくいというデメリットもあります。
詳細は私もよく分からないのですが、カスタマイズの方法を記録として残しておきたいと思います。
今回はgeneric.ListViewです。
※補足
いつもはdjangoプロジェクトの作成から始めていますが、今回は割愛します。というのも、今回実装する機能は以前の記事「DjangoのCRUD処理について」をクラスベース汎用ビューに置き換えたものなので、詳細はそちらをご覧頂ければ幸いです。
イメージ
ListViewを使って、モデルを取得し、一覧で表示します。
モデル
モデルはこのような感じです。
from django.db import models
from django.utils import timezone
class ReviewByClass(models.Model):
STARS = (
(1, '★'),
(2, '★★'),
(3, '★★★'),
(4, '★★★★'),
(5, '★★★★★'),
)
store_name = models.CharField('store_name', max_length=255)
title = models.TextField('title', max_length=255)
text = models.TextField('review', blank=True)
stars = models.IntegerField('stars', choices=STARS)
created_at = models.DateTimeField('created_at', default=timezone.now)
def __str__(self):
return self.title
ビュー
肝心のListViewは以下のような感じです。クラスはたったの2行です。
ご覧の通り、テンプレートファイルの名称さえ書かなくても機能してしまいます。便利な反面、どうやってカスタマイズしたら良いか分かりにくいです。
from .models import ReviewByClass
from django.views import generic # クラスベース汎用ビュー
class ReviewList(generic.ListView):
model = ReviewByClass
テンプレートファイル
テンプレートファイルは以下の様になります。
{% extends "base.html" %}
<!-- コンテンツ -->
{% block content %}
<div class="container">
<h1>Reviews By Class</h1>
<hr>
{% for reviewbyclass in reviewbyclass_list %}
<article>
<h3><a href="{% url 'reviewsByClass:reviewbyclass_detail' reviewbyclass.pk %}">{{ reviewbyclass.store_name }}</a></h3>
<p>{{ reviewbyclass.get_stars_display }} - {{ reviewbyclass.title }} - {{ reviewbyclass.created_at }}</p>
</article>
{% endfor %}
<a href="{% url 'reviewsByClass:reviewbyclass_create' %}">create review</a>
</div>
{% endblock %}
仕様: テンプレートファイルの名称
ListViewの場合、デフォルトで使用されるテンプレートファイルは「モデル名小文字_list.html」になります。
仕様: QuerySetの名称
テンプレートファイルに渡されるQuerySet(モデルインスタンス)は「モデル名小文字_list」または「object_list」になります。
リストの並び替え
上のテンプレートでリストを取り出すのにfor文を使っていますが、取り出す順番を変えたくなる事があります。取り出すリストの順番を並び替える方法は以下の通りです。
class ReviewList(generic.ListView)
model = ReviewClass
ordering = ('-stars', '-created_at')
# 別の書き方?
# queryset = ReviewByClass.objects.order_by('-stars', '-created_at')
starsフィールドを降順にした上で、星の数が同じものは作成日(created_at)が新しい投稿を上に表示されます。
get_orderingメソッドを上書きする方法もあります。これによって、昇順、降順を動的に制御することができるみたいです。
class ReviewList(generic.ListView):
model = ReviewByClass
def get_ordering(self):
a = 10
if a > 5:
return ('-stars', 'created_at')
else:
return '-creates_at'
データの受け渡し
views.pyからテンプレートファイルへデータを受け渡す方法は以下の通りです。
class ReviewList(generic.ListView):
model = ReviewByClass
ordering = ('-stars','-created_at')
extra_context = {'KEY': 'VALUE!!'}
get_context_dataメソッドを使用する方法もあります。こちらもget_orderingメソッドと同様にif文などと組み合わせる事ができます。
class ReviewList(generic.ListView):
model = ReviewByClass
ordering = ('-stars','-created_at')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['KEY'] = 'VALUE!!'
return context
テンプレートファイル側の受け方は以下の通りです。
<h1>Reviews By Class</h1>
<h1>{{ KEY }}</h1>
<hr>
<!-- 以下省略 -->
画面は以下のようになります。
QuerySetの絞り込み
テンプレートファイルへ渡すQuerySetを絞り込むこともできます。例えば、検索フォームのようなものがついている時などがあるそうです。このような場合は、get_queryメソッドを上書きする必要があります。
class ReviewList(generic.ListView):
model = ReviewByClass
def get_queryset(self):
queryset = super().get_queryset()
if True:
queryset = queryset.filter(stars=5)
# queryset = queryset.filter(title__contains="A")
return queryset
filterの使い方については、こちらとかが参考になるかも知れません。
参考
Djangoビギナーズブック: クラスベース汎用ビューの仕組みが詳しく書いてあります。