はじめに
Djangoの標準ビューの一つでモデルを単純に利用した表示を行うListViewが用意されています。
1種類のモデルを利用した画面の作成に有効ですが、使い方に迷ったので、まとめてみます。
環境
- Python 3.8
- Django 3.1
モデル
本記事では、画面表示するモデルとして下記を使用することにします。
class Article(models.Model):
title = models.CharField(max_length=128)
content = models.TextField()
is_published = models.BooleanField()
使用例
シンプルに使う
ListViewを継承したビュークラスに対して、単純にモデルとテンプレート名だけ指定する状態です。
クラス変数のmodels
に表示したいモデルの名前、template_name
にレンダリングに使用するテンプレートファイルをすると、指定したモデルの全ての保存済みオブジェクトを取得して、テンプレートでレンダリングするようになります。
この時、テンプレート側にはモデルのオブジェクトはobject_list
という名前で渡されます。
from django.views.generic import ListView
from .models import Article
class ArticleListView(Listview):
template_name = 'article_list.html'
model = Article
<!DOCTYPE html>
<html>
<head></head>
<body>
<!-- object_list は Article.objects.all() と同じ結果となる -->
{% for article in object_list %}
<li>{{ article.title }}</li>
{% endfor %}
</body>
</html>
テンプレートに渡すモデルを並び変える
ordering
というクラス変数を利用して、モデルの並び替えができます。
モデル操作時にしようするorder_by
に渡す並び替えに使用するキーを指定すると、そのキーで並び替えられた状態でテンプレートに渡されるようになります。
from django.views.generic import ListView
from .models import Article
class ArticleListView(Listview):
template_name = 'article_list.html'
model = Article
ordering = '-title' # order_by('-title')
モデルをフィルタ・絞り込みする
QuerySetを指定する方法
model
でモデルを指定する代わりにquery_set
にクエリを設定することができます。
これを利用すると、モデルのフィルタや絞り込み、並び替え等を行うことができます。
from django.views.generic import ListView
from .models import Article
class ArticleListView(Listview):
template_name = 'article_list.html'
query_set = Article.objects.filter(is_published=True) # is_publish が Trueのものに絞る
get_querysetメソッドを使う方法
query_set
にQuerySetを設定する方法では、単純なワンライナー程度の設定しかできませんでしたが、get_query_set
というメソッドをオーバーライドすることで、より自由度の高い実装が可能です。
リクエストのGETパラメータの内容によって動作を変えたり、非表示(論理削除)のモデルを除外する等の複雑な制御が可能です。
この関数は既存のメソッドを拡張する形となるので、最初にスーパークラス(ListView)のget_query_set
を実行して元になるQuerySetを取得します。
それをもとに必要な操作を行って加工を行い、最終的なQuerySetを戻り値として返すようにします。
このとき実行するスーパークラスのget_query_set
は、クラス変数のmodel
やordering
を設定値を利用した結果の断面のQuerySetが返ってきます。
from django.views.generic import ListView
from .models import Article
class ArticleListView(Listview):
template_name = 'article_list.html'
model = Article
def get_queryset(self, **kwargs):
queryset = super().get_queryset(**kwargs) # Article.objects.all() と同じ結果
# GETリクエストパラメータにkeywordがあれば、それでフィルタする
keyword = self.request.GET.get('keyword')
if keyword is not None:
queryset = queryset.filter(title__contains=keyword)
# is_publishedがTrueのものに絞り、titleをキーに並び変える
queryset = queryset.filter(is_published=True).order_by('-title')
return queryset
コンテキストを加工する
get_context_data
メソッドをオーバーライドすることで、テンプレートに渡すコンテキストを加工することもできます。
このメソッドも既存のListViewが持つメソッドをオーバーライドする形となりますので、最初にスーパークラスのget_context_data
を実行して元になるコンテキストデータを取得して加工します。
スーパークラスのメソッドは、object_list
(表示対象モデルのリスト)等が格納された状態のコンテキストが返ってくるため、それに対し要素の追加や変更を行いメソッドの戻り値として返します。
ここで返す結果はViewで自前でコンテキストに変数を設定してレンダリングする場合と同じように、テンプレート内で使用することができます。
from django.views.generic import ListView
from .models import Article
class ArticleListView(Listview):
template_name = 'article_list.html'
model = Article
def get_context_data(self):
ctx = super().get_context_data()
# page_title を追加する
ctx['page_title'] = 'hoge'
return ctx
<!DOCTYPE html>
<html>
<head></head>
<body>
<!-- 追加した変数を使用可能 -->
<h1>{{ page_title }}</h1>
{% for article in object_list %}
<li>{{ article.title }}</li>
{% endfor %}
</body>
</html>
テンプレートに渡すモデルのオブジェクト名を変える
テンプレートに渡されるオブジェクトはobject_list
という名前でしたが、クラス変数のcontext_object_name
に名前を指定することで、任意の名前に変更することもできます。
from django.views.generic import ListView
from .models import Article
class ArticleListView(Listview):
template_name = 'article_list.html'
model = Article
context_object_name = 'articles' # オブジェクト名を articles に指定
<!DOCTYPE html>
<html>
<body>
{% for article in articles %} <!-- object_list から articles に変更 -->
<li>{{ article.title }}</li>
{% endfor %}
</body>
</html>