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">« 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 »</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.