Django Pagination Tutorial with Example

Mehedi Khan
Django Unleashed
Published in
4 min readNov 11, 2024
Django Pagination Tutorial with Example

Django’s pagination module allows you to split a large set of data into smaller chunks (pages). This is particularly useful for displaying a list of items, such as blog posts or products, in a manageable way. Let’s dive into how to implement pagination step-by-step.

Table of Contents

  1. What is Pagination?
  2. Setup Django Project
  3. Setting Up Models
  4. Creating a View with Pagination
  5. Using Pagination in Templates
  6. Handling Edge Cases
  7. Final Example

1. What is Pagination?

Pagination is the process of dividing a large dataset into smaller sets or pages. Each page contains a limited number of items, and users can navigate through pages.

2. Setup Django Project

First, create & activate virtualenv and also create Django project and app if you haven’t already.

mkdir myproject
cd myproject

python -m venv venv # create virtualenv
source venv\bin\activate # activate the virtualenv

django-admin startproject myproject .
python manage.py startapp myapp

Update INSTALLED_APPS in your settings.py to include the app:

INSTALLED_APPS = [
# Other apps
'myapp',
]

3. Setting Up Models

Let’s create a simple Post model to display as paginated content.

# myapp/models.py
from django.db import models


class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()

def __str__(self):
return self.title

Now, make migrations and migrate the model:

python manage.py makemigrations
python manage.py migrate

You can also add some sample data in the Django admin panel or use the shell:

python manage.py shell
from myapp.models import Post

for i in range(1, 101):
Post.objects.create(title=f"Post {i}", content=f"Content for post {i}")

4. Creating a View with Pagination

Django provides the Paginator class for handling pagination. Let's create a view to paginate the Post objects.

# myapp/views.py
from django.core.paginator import Paginator
from django.shortcuts import render
from .models import Post


def post_list(request):
posts = Post.objects.all()
paginator = Paginator(posts, 10) # Show 10 posts per page.

page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)

return render(request, 'myapp/post_list.html', {'page_obj': page_obj})

Here, we use Paginator to divide the Post objects into pages of 10 posts each. We also retrieve the page_number from the URL query parameters (?page=2, etc.) and pass the page_obj to the template.

5. Using Pagination in Templates

Now, let’s create the template to display the paginated posts.

<!-- myapp/templates/myapp/post_list.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Paginated Post List</title>
</head>
<body>
<h1>Paginated Posts</h1>

<ul>
{% for post in page_obj %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>

<div class="pagination">
<span>
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>

{% if page_obj.has_previous %}
<a href="?page=1">First</a>
<a href="?page={{ page_obj.previous_page_number }}">Previous</a>
{% endif %}

{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<strong>{{ num }}</strong>
{% else %}
<a href="?page={{ num }}">{{ num }}</a>
{% endif %}
{% endfor %}

{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">Next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">Last</a>
{% endif %}
</div>
</body>
</html>

Explanation:

  • The for loop displays each post title from the page_obj.
  • We create pagination controls that show:
  • First and Previous buttons if there are previous pages.
  • Page numbers and the current page highlighted.
  • Next and Last buttons if there are more pages.

6. Handling Edge Cases

  1. Empty Pages: When a user requests a page that doesn’t exist (e.g., page=1000), Django raises an EmptyPage exception. You can handle this in your view:
# myapp/views.py
from django.core.paginator import EmptyPage, PageNotAnInteger

def post_list(request):
posts = Post.objects.all()
paginator = Paginator(posts, 10)

page_number = request.GET.get('page')
try:
page_obj = paginator.get_page(page_number)
except PageNotAnInteger:
page_obj = paginator.page(1)
except EmptyPage:
page_obj = paginator.page(paginator.num_pages)

return render(request, 'myapp/post_list.html', {'page_obj': page_obj})

2. Performance Considerations: When working with large datasets, pagination queries can become slow. Consider using select_related or prefetch_related to reduce database hits when dealing with related models.

7. Final Example

Let’s put everything together:

  1. Model: The Post model has been defined to store post titles and content.
  2. View: The post_list view uses Django's pagination to divide posts into pages of 10.
  3. Template: The template shows posts and includes pagination controls with links to navigate through pages.

Complete Code Example:

  1. Models (models.py):
# myapp/views.py
from django.db import models


class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()

def __str__(self):
return self.title

2. Views (views.py):

# myapp/views.py
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render
from .models import Post

def post_list(request):
posts = Post.objects.all()
paginator = Paginator(posts, 10)

page_number = request.GET.get('page')
try:
page_obj = paginator.get_page(page_number)
except PageNotAnInteger:
page_obj = paginator.page(1)
except EmptyPage:
page_obj = paginator.page(paginator.num_pages)

return render(request, 'myapp/post_list.html', {'page_obj': page_obj})

3. Templates (post_list.html):

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Paginated Post List</title>
</head>
<body>
<h1>Paginated Posts</h1>

<ul>
{% for post in page_obj %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>

<div class="pagination">
<span>
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>

{% if page_obj.has_previous %}
<a href="?page=1">First</a>
<a href="?page={{ page_obj.previous_page_number }}">Previous</a>
{% endif %}

{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<strong>{{ num }}</strong>
{% else %}
<a href="?page={{ num }}">{{ num }}</a>
{% endif %}
{% endfor %}

{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">Next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">Last</a>
{% endif %}
</div>
</body>
</html>

Conclusion

This tutorial covers the basics of Django pagination with examples. Pagination is essential for improving user experience when dealing with large data sets.

Thank you for reading! If you notice any kind of mistakes or have suggestions for improvement, please leave a comment below.

If you helpful this post, please click the 👏 button to help others discover it. You can also follow me on:

GitHub | daily.dev | LinkedIn | YouTube

More Libraries

Django

31 stories

Python

12 stories

--

--

Django Unleashed
Django Unleashed

Published in Django Unleashed

Unleashing the Full Potential of Web Development

Mehedi Khan
Mehedi Khan

Written by Mehedi Khan

I'm a Software engineer. I'm comfortable with Python, Django, and Full-stack Web Development. Follow To Support Me On Medium 🫠

Responses (1)