【初心者向け】Django views.generic.list.MultipleObjectMixin.paginate_queryset() を徹底解説!


タプルの要素

  1. paginator
    ページネーション処理を行うオブジェクト
  2. page
    現在表示されているページ
  3. object_list
    現在表示されているページのオブジェクトリスト
  4. is_paginated
    ページネーションが適用されているかどうか

使い方

def get(self, request, *args, **kwargs):
    queryset = self.get_queryset()
    page_size = self.get_paginate_by(queryset)
    if page_size:
        paginator, page, object_list, is_paginated = self.paginate_queryset(queryset, page_size)
        context = {
            "paginator": paginator,
            "page_obj": page,
            "object_list": object_list,
            "is_paginated": is_paginated,
        }
    else:
        context = {
            "object_list": queryset,
        }
    return self.render_to_response(context)

詳細

  • is_paginated
    ページネーションが適用されているかどうか
  • object_list
    現在表示されているページのオブジェクトリスト
  • page
    現在表示されているページ
  • paginator
    ページネーション処理を行うオブジェクト
  • page_size
    1ページあたりのオブジェクト数
  • queryset
    ページ化対象のクエリセット


from django.views.generic.list import ListView

class ArticleListView(ListView):
    model = Article
    paginate_by = 10

この例では、Article モデルのオブジェクトを10件ずつページ化して表示します。

  • paginate_queryset() メソッドは、django.core.paginator.Paginator クラスを使用しています。


基本的なページネーション

from django.views.generic.list import ListView

class ArticleListView(ListView):
    model = Article
    paginate_by = 10
  • is_paginated: ページネーションが適用されているかどうか
  • object_list: 現在表示されているページのオブジェクトリスト
  • page_obj: 現在表示されているページオブジェクト
  • paginator: ページネーションオブジェクト
{% if is_paginated %}
  <ul class="pagination">
    {% for page in paginator.page_range %}
      <li{% if page.is_current %} class="active"{% endif %}>
        <a href="{{ page.url }}">{{ page.number }}</a>
      </li>
    {% endfor %}
  </ul>
{% endif %}

<ul>
  {% for article in object_list %}
    <li>
      {{ article.title }}
    </li>
  {% endfor %}
</ul>

カスタムページネーションクラス

より高度なページネーション機能を実現するために、カスタムページネーションクラスを作成することができます。

from django.core.paginator import Paginator

class MyPaginator(Paginator):
    def get_page_num_range(self):
        """
        ページ番号の範囲を取得します。
        """
        num_pages = self.num_pages
        page_range = list(range(1, num_pages + 1))

        if num_pages <= 10:
            return page_range

        page = self.page_number
        half_window = 5

        if page < half_window:
            return list(range(1, half_window * 2 + 1))
        elif page > num_pages - half_window:
            return list(range(num_pages - half_window * 2, num_pages + 1))
        else:
            return list(range(page - half_window, page + half_window + 1))

class ArticleListView(ListView):
    model = Article
    paginate_by = 10
    paginator_class = MyPaginator

この例では、MyPaginator というカスタムページネーションクラスを作成しています。このクラスは、get_page_num_range メソッドをオーバーライドし、現在のページ番号周辺のページ番号のみを表示するようにしています。

検索機能付きページネーション

from django.db.models import Q

class ArticleListView(ListView):
    model = Article
    paginate_by = 10

    def get_queryset(self):
        keyword = self.request.GET.get("keyword")
        if keyword:
            return Article.objects.filter(Q(title__icontains=keyword) | Q(content__icontains=keyword))
        else:
            return super().get_queryset()

この例では、keyword パラメータを使用して検索機能を追加しています。検索キーワードがある場合は、そのキーワードを含む記事のみをページ化して表示します。

from django.http import JsonResponse

class ArticleListView(ListView):
    model = Article
    paginate_by = 10

    def get_template_name(self):
        """
        テンプレート名を返します。
        """
        if self.request.is_ajax():
            return "articles/partial_article_list.html"
        else:
            return "articles/article_list.html"

    def get_context_data(self, **kwargs):
        """
        コンテキストデータを返します。
        """
        context = super().get_context_data(**kwargs)

        if self.request.is_ajax():
            context["object_list"] = self.object_list.values()
            return JsonResponse(context)
        else:
            return context

この例では、Ajax リクエストの場合は部分テンプレートを使用してレスポンスを返し、それ以外の場合は通常のテンプレートを使用してレスポンスを返します。



代替方法の例

カスタムページネーションクラス

  • 特定の要件に合わせたページネーションロジックを実装したい場合
  • より高度なページネーション機能を実現したい場合


from django.core.paginator import Paginator

class MyPaginator(Paginator):
    def get_page_num_range(self):
        """
        ページ番号の範囲を取得します。
        """
        num_pages = self.num_pages
        page_range = list(range(1, num_pages + 1))

        if num_pages <= 10:
            return page_range

        page = self.page_number
        half_window = 5

        if page < half_window:
            return list(range(1, half_window * 2 + 1))
        elif page > num_pages - half_window:
            return list(range(num_pages - half_window * 2, num_pages + 1))
        else:
            return list(range(page - half_window, page + half_window + 1))

class ArticleListView(ListView):
    model = Article
    paginate_by = 10
    paginator_class = MyPaginator

手動でのページネーション

  • カスタムテンプレートを使用したい場合
  • 非常にシンプルなページネーションが必要な場合


from django.db import models

def article_list_view(request):
    page_size = 10
    page = int(request.GET.get("page", 1))
    start_index = (page - 1) * page_size
    end_index = start_index + page_size

    articles = Article.objects.all()[start_index:end_index]

    paginator = Paginator(Article.objects.all(), page_size)
    page_obj = paginator.get_page(page)

    context = {
        "object_list": articles,
        "paginator": paginator,
        "page_obj": page_obj,
    }
    return render(request, "articles/article_list.html", context)

サードパーティ製ライブラリ

  • 複雑なページネーションロジックを実装したい場合
  • より高度なページネーション機能が必要な場合
from django.views.generic import ListView
from rest_framework.pagination import PageNumberPagination

class ArticleListView(ListView):
    model = Article
    paginate_by = 10
    pagination_class = PageNumberPagination

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    pagination_class = PageNumberPagination