LoginSignup
52
39

More than 3 years have passed since last update.

【Django】ListViewの使い方と出来ること

Posted at

はじめに

Djangoの標準ビューの一つでモデルを単純に利用した表示を行うListViewが用意されています。
1種類のモデルを利用した画面の作成に有効ですが、使い方に迷ったので、まとめてみます。

環境

  • Python 3.8
  • Django 3.1

モデル

本記事では、画面表示するモデルとして下記を使用することにします。

models.py
class Article(models.Model):
    title = models.CharField(max_length=128)
    content = models.TextField()
    is_published = models.BooleanField()

使用例

シンプルに使う

ListViewを継承したビュークラスに対して、単純にモデルとテンプレート名だけ指定する状態です。
クラス変数のmodelsに表示したいモデルの名前、template_nameにレンダリングに使用するテンプレートファイルをすると、指定したモデルの全ての保存済みオブジェクトを取得して、テンプレートでレンダリングするようになります。
この時、テンプレート側にはモデルのオブジェクトはobject_listという名前で渡されます。

views.py
from django.views.generic import ListView
from .models import Article

class ArticleListView(Listview):
    template_name = 'article_list.html'
    model = Article
article_list.html
<!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に渡す並び替えに使用するキーを指定すると、そのキーで並び替えられた状態でテンプレートに渡されるようになります。

views.py
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にクエリを設定することができます。
これを利用すると、モデルのフィルタや絞り込み、並び替え等を行うことができます。

views.py
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は、クラス変数のmodelorderingを設定値を利用した結果の断面のQuerySetが返ってきます。

views.py
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で自前でコンテキストに変数を設定してレンダリングする場合と同じように、テンプレート内で使用することができます。

views.py
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
article_list.html
<!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に名前を指定することで、任意の名前に変更することもできます。

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' # オブジェクト名を articles に指定
article_list.html
<!DOCTYPE html>
<html>
<body>
  {% for article in articles %} <!-- object_list から articles に変更 -->
  <li>{{ article.title }}</li>
  {% endfor %}
</body>
</html>
52
39
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
39