Display only some of the page numbers by django pagination
Gonna throw this in. I came up with it because it lets you know there are more pages on either side.
<ul class="pagination">
{% if page_obj.has_previous %}
<li><a href="?page={{ page_obj.previous_page_number }}"><i class="fa fa-chevron-left" aria-hidden="true"></i></a></li>
{% else %}
<li class="disabled"><span><i class="fa fa-chevron-left" aria-hidden="true"></i></span></li>
{% endif %}
{% if page_obj.number|add:'-4' > 1 %}
<li><a href="?page={{ page_obj.number|add:'-5' }}">…</a></li>
{% endif %}
{% for i in page_obj.paginator.page_range %}
{% if page_obj.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% elif i > page_obj.number|add:'-5' and i < page_obj.number|add:'5' %}
<li><a href="?page={{ i }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.paginator.num_pages > page_obj.number|add:'4' %}
<li><a href="?page={{ page_obj.number|add:'5' }}">…</a></li>
{% endif %}
{% if page_obj.has_next %}
<li><a href="?page={{ page_obj.next_page_number }}"><i class="fa fa-chevron-right" aria-hidden="true"></i></a></li>
{% else %}
<li class="disabled"><span><i class="fa fa-chevron-right" aria-hidden="true"></i></span></li>
{% endif %}
</ul>
And it looks like this:
Another shorter solution with template is compare current forloop.counter with certain range.
with bootstrap I use this template
<nav aria-label="Page navigation"> <ul class="pagination"> {% if page_obj.has_previous %} <li class="page-item"> <a class="page-link" href="?page=1" aria-label="Previous"> <span aria-hidden="true">«</span> <span class="sr-only">begin</span> </a> </li> {% endif %} {% for n in page_obj.paginator.page_range %} {% if page_obj.number == n %} <li class="page-item active"> <span class="page-link">{{ n }}<span class="sr-only">(current)</span></span> </li> {% elif n > page_obj.number|add:'-3' and n < page_obj.number|add:'3' %} <li class="page-item"><a class="page-link" href="?page={{ n }}">{{ n }}</a></li> {% endif %} {% endfor %} {% if page_obj.has_next %} <li class="page-item"> <a class="page-link" href="?page={{ page_obj.paginator.num_pages }}" aria-label="Next"> <span aria-hidden="true">»</span> <span class="sr-only">end</span> </a> </li> {% endif %} </ul> </nav>
First of all I would change the following:
try:
blogs = paginator.page(page)
except(EmptyPage, InvalidPage):
blogs = paginator.page(page) # Raises the same error
But you could pass a range within your context.
index = paginator.page_range.index(blogs.number)
max_index = len(paginator.page_range)
start_index = index - 3 if index >= 3 else 0
end_index = index + 3 if index <= max_index - 3 else max_index
page_range = paginator.page_range[start_index:end_index]
Now you should be able to loop over the range to construct the right links with ?page=
.
=== Edit ===
So your view would be something like this:
def blog(request):
paginator = Paginator(Blog.objects.all(), 1)
try:
page = int(request.GET.get('page', '1'))
except:
page = 1
try:
blogs = paginator.page(page)
except(EmptyPage, InvalidPage):
blogs = paginator.page(1)
# Get the index of the current page
index = blogs.number - 1 # edited to something easier without index
# This value is maximum index of your pages, so the last page - 1
max_index = len(paginator.page_range)
# You want a range of 7, so lets calculate where to slice the list
start_index = index - 3 if index >= 3 else 0
end_index = index + 3 if index <= max_index - 3 else max_index
# Get our new page range. In the latest versions of Django page_range returns
# an iterator. Thus pass it to list, to make our slice possible again.
page_range = list(paginator.page_range)[start_index:end_index]
return render(request, 'blogs.html', {
'blogs': blogs,
'page_range': page_range,
})
So now we have to edit your template to accept our new list of page numbers:
<div class="prev_next">
{% if blogs.has_previous %}
<a class="prev btn btn-info" href="?page={{blogs.previous_page_number}}">Prev</a>
{% endif %}
{% if blogs.has_next %}
<a class="next btn btn-info" href="?page={{blogs.next_page_number}}">Next</a>
{% endif %}
<div class="pages">
<ul>
{% for pg in page_range %}
{% if blogs.number == pg %}
<li><a href="?page={{pg}}" class="btn btn-default">{{pg}}</a></li>
{% else %}
<li><a href="?page={{pg}}" class="btn">{{pg}}</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
<span class="clear_both"></span>
</div>
I found the simplest thing to do was to create a pagination snippet that just shows the pages you want it to.
In my case I didn't want any previous or next links. I just wanted to always have a link to the first and last pages and then have the current page and the two pages either side of the current page.
My template snippet (uses variables from django-tables2 - variables will have slightly different names if you're using a Django Paginator
directly)
{% load django_tables2 %}
{% load humanize %}
{% load i18n %}
{% if table.page %}
{% with table.page.paginator.count as total %}
{% with table.page.number as page_num %}
{% with table.page.paginator.num_pages as num_pages %}
{% block pagination %}
<div class="row">
<div class="col-md-12">
{% if table.paginator.num_pages > 1 %}
<ul class="pagination pull-right">
{% for n in table.page.paginator.page_range %}
{% if table.page.number|add:'-3' == n %}
{# First page #}
<li><a href="{% querystring table.prefixed_page_field=1 %}">1</a></li>
{% if n != 1 %}
<li class="disabled"><a>⋯</a></li>
{% endif %}
{% elif table.page.number == n %}
{# Current page #}
<li class="active"><a href="#">{{ n }}</a></li>
{% elif table.page.number|add:'-3' < n and n < table.page.number|add:'3' %}
{# Pages around current page #}
<li><a href="{% querystring table.prefixed_page_field=n %}">{{ n }}</a></li>
{% elif table.page.number|add:'3' == n %}
{# Last page #}
{% if n != num_pages %}
<li class="disabled"><a>⋯</a></li>
{% endif %}
<li><a href="{% querystring table.prefixed_page_field=num_pages %}">{{ num_pages }}</a></li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endblock pagination %}
{% endwith %}
{% endwith %}
{% endwith %}
{% endif %}
Examples of what my pagination looks like at different pages
Credit: this was inspired by @Pavel1114's answer.