Using Django Pagination

June 4, 2020, 4:48 p.m.

Django Bootstrap · 9 min read

Using Django Pagination

Django's Pagination allows you to handle data divided across several pages using "Previous" and "Next" links.

The Paginator class takes in a queryset (such as a Django model) and splits it into Page objects that are then displayed across these pages while still using the same HTML document.

Pagination works well for listing articles, products, and/or charts and is easy to implement on your template pages. Connect the Paginator class to a views function then call the Pagination class attribute in the template and Django Pagination will handle generating the pages as needed. 

 

Add Paginator to a views function

env > mysite > main > views.py

from django.shortcuts import render
from .models import Movie

# Create your views here.
def movies(request):
	movies = Movie.objects.all() #queryset containing all movies we just created
	return render(request=request, template_name="main/movies.html", context={'movies':movies})

We will be using the Movie model from the tutorial How to use Django Models. Above is how the view function looked at the end of the tutorial. 

 

...
from django.core.paginator import Paginator #import Paginator


def movies(request):
	movies = Movie.objects.all() #queryset containing all movies we just created
	paginator = Paginator(movies, 3)
	page_number = request.GET.get('page')
	page_obj = paginator.get_page(page_number)
	return render(request=request, template_name="main/movies.html", context={'movies':page_obj})

Start by importing Paginator at the top of the page. Then add three new variables to the movies function.

First, create a paginator object with the Paginator class. As arguments pass in the movies queryset and number of movies per page, 3. Then create a page_number variable that requests the current page number. Next, combine all of the data into page_obj.

Finally, we need to set the context movies to equal page_obj before moving on to the template. That way all of the model fields called in the template can still be called using the context movies but with all of the pagination functionality.

 

Add pagination to the template

env > mysite > main > templates > main > movies.html

<!DOCTYPE html>
<html>
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Movies</title>
    <!--Bootstrap CSS-->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
  </head>
  <body>


    <div class="container p-4">
      <div class="row">
        {% for m in movies %}
        <div class="col-lg-4 col-md-6 col-sm-12 pb-4">
          <div class="card h-100 p-4">
            <img src="{{ m.movie_poster.url }}" class="card-img-top" alt="{{ m.movie_title}}" style="width: auto; height: 350px; object-fit: contain;">
            <br>
            <h4>{{m.movie_title}}</h4>
            <p class="text-muted">{{m.release_year}} | {{m.director}}</p>
            <p>{{m.movie_plot}}</p>
          </div>
        </div>
        {% endfor %}
      </div>
    </div>

    <!--Pagination-->
      <div class="container p-4">
        <div class="pagination justify-content-center">
            <span class="step-links">
              {% if movies.has_previous %}
                  <a href="?page=1">&laquo; first</a>
                  <a href="?page={{ movies.previous_page_number }}">previous</a>
              {% endif %}

                <span class="current">
                    Page {{ movies.number }} of {{ movies.paginator.num_pages }}
                </span>

              {% if movies.has_next %}
                  <a href="?page={{ movies.next_page_number }}">next</a>
                  <a href="?page={{ movies.paginator.num_pages }}">last &raquo;</a>
              {% endif %}
            </span>
            </div>
        </div>
      <!--end of Pagination-->


    <!-- Optional Javascript -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
  </body>
</html>

Given that we did not change the context name, there is no need to change anything within the for loop. However, we do need to add some HTML code to display pagination in the templates. We will have the links for "first", "previous", "next", "last", along with "Page (current) of (total pages)" displayed at the bottom of the template. We are using the Bootstrap CDN for this template but the Pagination HTML format is from the Django documentation.

 

If you open your HTML template in the browser, you can now see that only three movie posters are displaying with Pagination links at the bottom of the page. Now if you click the "next" button you will be brought to page 2 where the last movie poster is displayed. 

Django default pagination

 

Display all page numbers as links

env > mysite > main > templates > main > movies.html

<!DOCTYPE html>
<html>
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Movies</title>
    <!--Bootstrap CSS-->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
  </head>
  <body>


    <div class="container p-4">
      <div class="row">
        {% for m in movies %}
        <div class="col-lg-4 col-md-6 col-sm-12 pb-4">
          <div class="card h-100 p-4">
            <img src="{{ m.movie_poster.url }}" class="card-img-top" alt="{{ m.movie_title}}" style="width: auto; height: 350px; object-fit: contain;">
            <br>
            <h4>{{m.movie_title}}</h4>
            <p class="text-muted">{{m.release_year}} | {{m.director}}</p>
            <p>{{m.movie_plot}}</p>
          </div>
        </div>
        {% endfor %}
      </div>
    </div>

    <!--Pagination-->
      <div class="container p-4">
        <div class="pagination justify-content-center">
              {% if movies.has_previous %}
                   <li><a href="?page={{ movies.previous_page_number }}">&laquo;</a></li>
              {% else %}
                <li class="disabled"><span>&laquo;</span></li>
              {% endif %}
              {% for i in movies.paginator.page_range %}
                {% if movies.number == i %}
                  <li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
                {% else %}
                  <li><a href="?page={{ i }}">{{ i }}</a></li>
                {% endif %}
              {% endfor %}
              {% if movies.has_next %}
                <li><a href="?page={{ movies.next_page_number }}">&raquo;</a></li>
              {% else %}
                <li class="disabled"><span>&raquo;</span></li>
              {% endif %}
        </div>
      </div>
    <!--end of Pagination-->


    <!-- Optional Javascript -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
  </body>
</html>

If you want to list all of the pages as links, you can use the code above. 

https://d2gdtie5ivbdow.cloudfront.net/articles/pagination_django_default.PNG

 

Display all page numbers as links with Bootstrap Pagination

env > mysite > main > templates > main > movies.html

<!DOCTYPE html>
<html>
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Movies</title>
    <!--Bootstrap CSS-->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
  </head>
  <body>


    <div class="container p-4">
      <div class="row">
        {% for m in movies %}
        <div class="col-lg-4 col-md-6 col-sm-12 pb-4">
          <div class="card h-100 p-4">
            <img src="{{ m.movie_poster.url }}" class="card-img-top" alt="{{ m.movie_title}}" style="width: auto; height: 350px; object-fit: contain;">
            <br>
            <h4>{{m.movie_title}}</h4>
            <p class="text-muted">{{m.release_year}} | {{m.director}}</p>
            <p>{{m.movie_plot}}</p>
          </div>
        </div>
        {% endfor %}
      </div>
    </div>

    <!--Pagination-->
    <nav aria-label="Page navigation example">
      <ul class="pagination justify-content-center">
        {% if movies.has_previous %}
          <li class="page-item">
            <a class="page-link" href="?page={{ movies.previous_page_number }}">Previous</a>
          </li>
        {% else %}
          <li class="page-item disabled">
            <a class="page-link" href="#" tabindex="-1" aria-disabled="true">Previous</a>
          </li>
        {% endif %}
        {% for i in movies.paginator.page_range %}
          {% if movies.number == i %}
            <li class="page-item active" aria-current="page">
              <span class="page-link">
                {{ i }}
                <span class="sr-only">(current)</span>
              </span>
            </li>
          {% else %}
            <li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
          {% endif %}
        {% endfor %}
        {% if movies.has_next %}
          <li class="page-item">
            <a class="page-link" href="?page={{ movies.next_page_number }}">Next</a>
          </li>
        {% else %}
          <li class="page-item disabled">
            <a class="page-link" href="#" tabindex="-1" aria-disabled="true">Next</a>
          </li>
        {% endif %}
      </ul>
    </nav>
    <!--end of Pagination-->



    <!-- Optional Javascript -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
  </body>
</html>

And if you want to add Bootstrap's CSS to your Pagination, use this code. It has the same functionality as the previous, only now with some added CSS.

Bootstrap Pagination

 

Accounting for many pages when display all page numbers as links

env > mysite > main > templates > main > movies.html

<!DOCTYPE html>
<html>
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Movies</title>
    <!--Bootstrap CSS-->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
  </head>
  <body>


    <div class="container p-4">
      <div class="row">
        {% for m in movies %}
        <div class="col-lg-4 col-md-6 col-sm-12 pb-4">
          <div class="card h-100 p-4">
            <img src="{{ m.movie_poster.url }}" class="card-img-top" alt="{{ m.movie_title}}" style="width: auto; height: 350px; object-fit: contain;">
            <br>
            <h4>{{m.movie_title}}</h4>
            <p class="text-muted">{{m.release_year}} | {{m.director}}</p>
            <p>{{m.movie_plot}}</p>
          </div>
        </div>
        {% endfor %}
      </div>
    </div>

    <!--Pagination-->
    <nav aria-label="Page navigation example">
        <ul class="pagination justify-content-center">
        {% if movies.has_previous %}
            <li class="page-item">
            <a class="page-link" href="?page={{ movies.previous_page_number }}">Previous</a>
          </li>
        {% else %}
            <li class="page-item disabled">
            <a class="page-link" href="#" tabindex="-1" aria-disabled="true">Previous</a>
          </li>
        {% endif %}

        {% if movies.number|add:'-4' > 1 %}
            <li class="page-item"><a class="page-link" href="?page={{ movies.number|add:'-5' }}">&hellip;</a></li>
        {% endif %}

        {% for i in movies.paginator.page_range %}
            {% if movies.number == i %}
                <li class="page-item active" aria-current="page">
              <span class="page-link">
                {{ i }}
                <span class="sr-only">(current)</span>
              </span>
            </li>
            {% elif i > movies.number|add:'-5' and i < movies.number|add:'5' %}
                 <li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
            {% endif %}
        {% endfor %}

        {% if movies.paginator.num_pages > movies.number|add:'4' %}
           <li class="page-item"><a class="page-link" href="?page={{ movies.number|add:'5' }}">&hellip;</a></li>
        {% endif %}

        {% if movies.has_next %}
            <li class="page-item">
            <a class="page-link" href="?page={{ movies.next_page_number }}">Next</a>
          </li>
        {% else %}
            <li class="page-item disabled">
            <a class="page-link" href="#" tabindex="-1" aria-disabled="true">Next</a>
          </li>
        {% endif %}
      </ul>
    </nav>
    <!--end of Pagination-->



    <!-- Optional Javascript -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
  </body>
</html>

If you choose to display all of the page numbers as links rather than going the Page 1 of 100 route, you need to configure your pagination to limit the number of links displayed per page. This will prevent the page from looking overrun with links once you get past 10 or so pages.

The code above allows a maximum of 13 links at the bottom of the page while updating the list of links as the user clicks through the pages.

Updated Bootstrap

Hopefully, you find this tutorial helpful!


0
Subscribe now

Subscribe to stay current on our latest articles and promos





Post a Comment
Join the community

0 Comments