Paginate with Django

Paginate with Django

Hello, so you have this long list of blog posts which you don't want to burden the users by returning all the posts at once and so you have decided to return just six (6) posts or more at a time but you are stuck on how to do that. Don't worry Django got you covered with Pagination.

Pagination in Django allows you to break a query list into pages and return the list contents page by page as requested by the user. Yes, the user can decide to go to any page if such a page exists.

Pagination in django API shell

Import the pagination class, create a list of objects to break into sublists or pages then create the paginator object as shown below

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

#create a list to paginate
my_lists = ['Apple', 'Orange', 'Mango', 'Pineapple', 'Lemon']

#paginator object 
my_pages = Paginator(my_lists, 3)

The Paginator class above takes in two major parameters i.e. the list to paginate and how many elements of the list should each page has.

As shown above, we have told it to give three elements per page but we only have five elements and thus it will only have 2 elements on the second page.

Below are the methods you can call on the paginator object we have created

  • count: This gives the total number of elements in the list given to the Paginator
my_pages.count

#output
5
  • num_pages: This will give you the number of pages the Paginator has broken the list into
my_pages.num_pages

#output
2
  • get_page: this returns a valid page and to get a valid result kindly input an integer, though it can accept a string as a parameter it will always give the first page, and if an integer is given as a page number which is out of range, it will provide the last page as a result
#cheack for a valid page 1
my_pages.page(1)
#output
<Page 1 of 2>

my_pages.page(2)
#output 
<Page 2 of 2>

my_pages.page("one")
#output
<Page 1 of 2>

#The result below will return the last page since page three does not exist
my_pages.page(3)
<Page 2 of 2>
  • validate_number: This is used to validate a page number, it takes in an integer of an integer in a quote and thus it returns the page number if such page exists else it returns an EmptyPage error
my_pages.validate_number(1)
#output
1
my_pages.validate("1")
#output
1
my_pages.validate_number(3)
#output
EmptyPage:That page contains no results

Now let's create an object instance for page 1 and see methods we can apply to each page

page1 = my_pages.page(1)

#calling page 1 gives the output
<Page 1 of 2>

methods on a page object

The following methods can be applied to the page object created

  • object_list: This is used to get the list of elements on the page
page1.object_list
#output
['Apple', 'Orange', 'Mango']
  • number: This gives the page number i.e it returns the page number of the associated page
page1.number
#output
1
  • paginator: This is used to reference the page to the attached paginator class
page1.paginator.num_pages
#output
2
  • has_next and next_page_number: The former will return a boolean value True or False to track if there exists a page after the current page and the latter will return the next page number if such page has a number
#note the parenthesis attached

page1.has_next()
#output
True

page1.next_page_number()
#output
2
  • has_previous and previous_next_page: The former will return boolean value True or False to track if there exists a page before the current page and the latter will return the previous page number if such page has a number.
#note the parenthesis attached

page1.has_previous())
#output
False

page1.next_page_number()
#output an error message
django.core.paginator.EmptyPage: That page number is less than 1
  • start_index and end_index: The former will give the 1-based index of the first element on the page list while the latter gives the 1-based index of the last element
page1.start_index()
#output 
1

page1.end_index()
#output
3

Notice the index count starts from 1 and not 0 i.e 1-based index

other methods include: has_other_pages etc

Pagination in Function Based Views

Assuming we have a view that gets the list of blogs and thus we only want to show six(6) blogs at a time then we can have logic as shown below

from django.core.paginator import Paginator
from .models import blog

def blog_posts(request):
    #Get the blog posts list
    blog_list = blog.objects.all()

    #Pass the list into the Paginator class with the number 6 to get 6 posts per page
    paginator = Paginator(blog_list, 6)

    #Get the current page number which will be updated as we navigate the pages
    page_number = request.GET.get('page')

    #get the current page content i.e posts to pass to the templates for display
    current_page = paginator.page(page_number)

    return render(request, 'blog_posts.html', {'current_page':current_page}

In the blog_posts.html we can then have the code below

{% for post in current_page %}
    {{ post.title|upper }}<br>
    {{post.content}}<br>
    {{post.date_published}}
{% endfor %}

<div class="pagination">
    <span class="step-links">
        {% if current_page.has_previous %}
            <a href="?page=1">&laquo; first</a>
            <a href="?page={{ current_page.previous_page_number }}">previous</a>
        {% endif %}

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

        {% if current_page.has_next %}
            <a href="?page={{ current_page.next_page_number }}">next</a>
            <a href="?page={{ current_page.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>
</div>

Pagination in class-based views

Pagination is much easier with a class-based view as we just have to replace the view definition as shown below

from django.views.generic import ListView

from .models import blog

class ContactListView(ListView):
    paginate_by = 6
    model = blog
    context_object_name = "current_page"

And that's it our blog post can now be viewed six at a time. Thank you.