Django: ListViewとDetailView

この記事の内容
  • DjangoのViewについて
  • views.pyについて
  • ListViewの使い方
  • DetailViewの使い方

この記事の対象者
  • Web開発に興味を持っている方
  • Djangoの学習者
  • Pythonエンジニア

この記事の構成
  1. ListView
  2. DetailView
  3. 基本的なクラスビューを振り返る

前回までの記事

Django: DBを扱うmodelsについて

Django: URLの設計について

Django: Viewの基本とTemplate View

Django: UpdateViewとCreateView

はじめに

前回の記事では、DjangoのUpdateView, CreateViewについて学習しました。

前提条件として、
Modelについての記事で作成したmodels.pyと
URLについての記事で作成したurls.pyを以下に共有しておきます。

models.py

from django.db import models


class Category(models.Model):

    name = models.CharField(max_length=31, verbose_name="カテゴリ名")

    def __str__(self):
        return self.name

    class Meta:

        verbose_name_plural = "カテゴリ"


class Article(models.Model):

    title = models.CharField(max_length=62, verbose_name="タイトル")
    slug = models.SlugField(verbose_name="URLスラッグ(英語)")
    image = models.ImageField(upload_to="article_image", blank=True, verbose_name="記事のイメージ写真", help_text="登録しない場合は、デフォルトのイメージ写真を使用する")
    categories = models.ManyToManyField(Category, verbose_name="カテゴリ")
    body = models.TextField(verbose_name="本文")
    status = models.PositiveSmallIntegerField(default=1, verbose_name="公開ステータス", help_text="1:下書き, 2:公開")
    liked = models.IntegerField(default=0, verbose_name="いいね数")
    is_deleted = models.BooleanField(default=False, verbose_name="削除フラグ")
    creator = models.CharField(max_length=31, verbose_name="投稿者")
    created = models.DateTimeField(auto_now_add=True, verbose_name="投稿日時")
    modified = models.DateTimeField(auto_now=True, verbose_name="更新日")

    def __str__(self):
        return self.title

    class Meta:

        verbose_name_plural = "記事"



class Comment(models.Model):

    article = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="記事", related_name="comments")
    commenter = models.CharField(max_length=31, verbose_name="コメント者名")
    body = models.TextField(verbose_name="コメント文")
    created = models.DateTimeField(auto_now_add=True, verbose_name="コメント投稿日時")

    def __str__(self):
        return self.article.title + ":{}のコメント".format(self.commenter)

    class Meta:

        verbose_name_plural = "コメント"

urls.py

  • index (hogehoge.com) ダッシュボード
  • create (hogehoge.com/article/create) 記事作成ページ
  • update (hogehoge.com/article/update/<int:pk>) 記事更新ページ
  • list (hogehoge.com/article/list) 記事一覧ページ
  • detail (hogehoge.com/article/<int:pk>) 記事詳細ページ
from django.urls import path
from . import views

app_name= 'article'
urlpatterns = [
    path('', views.index, name='index'),
    # 以下2つ追加
    path('article/create', views.article_create, name='article_create'),
    path('article/update/<int:pk>', views.article_update, name='article_update'),

    path('article/list', views.article_list, name='article_list'),
    path('article/<int:pk>', views.article_detail, name='article_detail'), 
]

ListView

では、ListViewについてみていきましょう。
ListViewは、指定したデータを複数取得するためのクラスビューです。
model = Article とすると Articleのデータを全て取得します。
ブログで言う、記事の一覧ページなどに使用することができます。

ListViewで指定できる変数

  • model: models.pyのクラス
  • queryset: modelの代わりにobjects.filter()などを記述することができる。 この場合、modelの記述は不要
  • paginate_by: ページネーションをかけて、指定した数だけのデータを送ることができる
  • 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
    paginate_by = 15
    context_object_name = 'articles'

article_list = ArticleListView.as_view()

paginate_by に数字を指定すると、その数ごとに区分けしてデータを取得します。
たったこれだけ指定することでページネーションができるようになります。

フロント側に関しては、ページネーション部分の調整が少しだけ必要です。

Django ページネーションのHTML用テンプレート

ListViewにおけるデータの取得

ListViewを用いれば、フロント側で、object_listとすることで全てのデータを取得することができます。これを
{% for object in object_list %}
とするケースが多いです。

ただしDjango側は、このobject_listと言う名前を明示的な名前に変更して使うことを推奨しており、
ArticleListViewに記述しているように
context_object_name に名前を入れることで、object_listという名前から指定した名前に変更することができます。

このケースの場合
{% for article in articles %}
のようにフロント側で記述することができます。

DetailView

DetailViewはURLに指定したユニークな情報から、1つだけ特定のデータを取得してくるクラスビューです。
ブログで言う記事の閲覧ページですね。
基本的にはid/pk(Primary Key)をURLに入れて、そこから特定のデータを取得します。
ただし、これを日付やURLスラッグなどに変更することも可能です。

DetailViewで指定できる変数

  • model: models.pyのクラス
  • queryset: modelの代わりにobjects.filter()などを記述することができる。 この場合、modelの記述は不要
  • context_object_name: デフォルトのデータ名を変更することができる
  • slug_field: URLに指定してあるデータの型を変更できる(デフォルトはint)
  • slug_url_kwarg: URLに指定してあるデータの名前を変更できる(デフォルトはpk)

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'

article_detail = ArticleDetailView.as_view()

基本的には、フロント側で{{ object }}とすることでデータを取得できますが、context_object_nameを指定してあるので、{{ article }}でデータを取得できます。
明示的になって良いですね。

基本的なクラスビューを振り返る

これまでTemplateViewに始まる基本的なクラスビューを解説してきました。
以下に簡単にもう一度おさらいしましょう。

Template View

ページを表示するView
データを取得せず、HTMLだけが必要な場合に使用。

CreateView

データを作成するView
フォームを自動生成して、データ保存の一覧の流れをデフォルトで行ってくれる

UpdateView

データを更新するVIew
基本的には、CreateViewと同じ。(但し既存のデータを取得して更新するため、URLにIDが必要)

ListView

データを複数取得するView
指定したクエリのデータを全て取得してそのままフロント側に流してくれる。ページネーションなどがコード1行で実装できる

DetailView

特定のデータを複数取得するView
URLなどに特定の情報(IDなど)を指定することで、特定のデータを一つだけ取得してくれる

終わりに

TemplateVIewの記事に始まり、Djangoのジェネリッククラスビューの基本的な機能についてみてきました。今回は、データの一覧と閲覧のListViewとDetailViewについて解説しました。

クラスビューにはできることがまだまだ無限にあります。
継承やMixinの活用、API化など。

それらの機能を活用することでDjangoの実装の幅がグッと高まります。

これからもどんどんDjangoのWeb開発について解説していきます。

お疲れ様でした!