Code Reference

Pagination

Django · Reference cheat sheet

Pagination

Django · Reference cheat sheet


📋 Overview

Paginator splits a queryset or sequence into pages. Use in function views, ListView (paginate_by), or DRF pagination classes. Prefer queryset pagination (LIMIT/OFFSET or keyset) over loading all rows into memory.

🔧 Core concepts

PieceRole
Paginator(object_list, per_page)Build pages
page(number)Page object
Page.object_listItems on page
has_next / has_previousNavigation
ListView.paginate_byCBV helper
InvalidPage / EmptyPage / PageNotAnIntegerErrors

DRF: PageNumberPagination, LimitOffsetPagination, CursorPagination.

💡 Examples

Function view:

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render

from .models import Article


def article_list(request):
    qs = Article.objects.filter(is_published=True).order_by("-pk")
    paginator = Paginator(qs, 20)
    page_number = request.GET.get("page")
    try:
        page = paginator.page(page_number)
    except PageNotAnInteger:
        page = paginator.page(1)
    except EmptyPage:
        page = paginator.page(paginator.num_pages)
    return render(request, "blog/list.html", {"page": page})

ListView:

from django.views.generic import ListView


class ArticleList(ListView):
    model = Article
    paginate_by = 20
    queryset = Article.objects.filter(is_published=True)

Template:

{% for article in page %}
  <li>{{ article.title }}</li>
{% endfor %}
{% if page.has_previous %}
  <a href="?page={{ page.previous_page_number }}">Prev</a>
{% endif %}
{% if page.has_next %}
  <a href="?page={{ page.next_page_number }}">Next</a>
{% endif %}

⚠️ Pitfalls

  • Unordered querysets → unstable pages; always order_by.
  • Large OFFSET is slow—consider cursor/keyset pagination for deep pages.
  • Counting huge tables on every request—cache count or approximate.
  • Mixing GET filters without preserving querystring in page links.
  • Evaluating full queryset before paginating.

On this page