← Back to Django Course | Chapter 9: Class-Based Views | Lesson 10 of 10

Pagination in Django

Pagination is like splitting a giant book into numbered pages so readers only see one page of items at a time instead of one endless scroll.

Paginating a ListView

Setting paginate_by on a ListView is enough to split the queryset into pages of that size; Django handles slicing the data and figuring out the current page from a ?page= query parameter automatically.

Example: Paginating a ListView

paginate_by = 10 makes BookListView show 10 books per page, reading the page number from ?page= in the URL.

markup
from django.views.generic import ListView
from .models import Book


class BookListView(ListView):
    model = Book
    paginate_by = 10
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

Rendering Page Navigation

When paginate_by is set, the template gets page_obj (representing the current page) and is_paginated (True when there is more than one page), which together are enough to build Previous/Next links.

Example: Rendering Page Navigation

page_obj.has_previous/has_next and their matching page numbers drive the Previous/Next links shown to the user.

markup
{% for book in page_obj %}
  <p>{{ book.title }}</p>
{% endfor %}

{% if is_paginated %}
  {% if page_obj.has_previous %}
    <a href="?page={{ page_obj.previous_page_number }}">Previous</a>
  {% endif %}
  {% if page_obj.has_next %}
    <a href="?page={{ page_obj.next_page_number }}">Next</a>
  {% endif %}
{% endif %}
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

Manual Pagination with the Paginator Class

For a function-based view, Django's Paginator class provides the same splitting logic directly: give it a queryset and a page size, then ask it for a specific page.

Example: Manual Pagination with the Paginator Class

Paginator(Book.objects.all(), 10).get_page(...) produces the same page_obj a ListView would build automatically.

markup
from django.core.paginator import Paginator
from django.shortcuts import render
from .models import Book


def book_list(request):
    paginator = Paginator(Book.objects.all(), 10)
    page_obj = paginator.get_page(request.GET.get('page'))
    return render(request, 'books/book_list.html', {'page_obj': page_obj})
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}
Common Mistakes
  1. Forgetting that paginate_by only works automatically on ListView — a manual view needs the Paginator class used directly.
  2. Looping over object_list in the template while expecting pagination to be active without checking is_paginated or using page_obj.
  3. Not adding "Previous"/"Next" links in the template, leaving pagination working in the backend but unreachable for users.
Chapter Summary
  • Pagination splits a long list of objects into smaller pages instead of showing everything at once.
  • ListView paginates automatically once paginate_by is set to a page size.
  • The template receives page_obj (the current page) and is_paginated (a boolean) to build navigation.
  • Django's Paginator class can be used directly in function-based views for the same behavior.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.