Last active
October 3, 2021 19:11
-
-
Save fmalina/1192bdabd9709008b689efa257ec4ca5 to your computer and use it in GitHub Desktop.
Pagination for Django reusable at view level (simple, generic)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Generic pagination reusable at view level. | |
Paging middleware needs installing in the settings: | |
MIDDLEWARE = ( | |
... almost at the end... | |
'paging.paging_middleware', | |
Use in a view like so: | |
from paging import simple_paging | |
def listings(request): | |
ls = Listing.objects.all() | |
ls, count, paging = simple_paging(request, ls, 100) | |
return render(request, 'some_listings.html', { | |
'ls': ls, 'count': count, 'paging': paging | |
}) | |
Include paging in your listings template: | |
{{ paging }} | |
""" | |
from django.core.paginator import Paginator, EmptyPage | |
from django.http import HttpResponseRedirect | |
from django.template.loader import render_to_string | |
def paging_middleware(get_response): | |
def middleware(request): | |
ext = '' | |
try: | |
if request.GET.get('page'): | |
request.page = int(request.GET.get('page', 1)) | |
else: | |
ext = request.path_info.split('.')[1] | |
request.page = int(ext) | |
except IndexError: | |
request.page = 1 | |
except ValueError: # redirect invalid page numbers to root page | |
if ext not in ('xml', 'csv'): | |
return HttpResponseRedirect(request.path_info.split('.')[0]) | |
response = get_response(request) | |
return response | |
return middleware | |
def simple_paging(request, qs, limit, page=None, custom_render=False): | |
pager = Paginator(qs, limit) | |
try: | |
page_obj = pager.page(page or request.page) | |
qs = page_obj.object_list | |
except EmptyPage: | |
page_obj = {} | |
qs = qs.none() | |
pages = pager.page_range | |
count = pager.count | |
if custom_render: | |
return qs, count, pages | |
paginate = render_paging(request, pages, page_obj, count, limit) | |
return qs, count, paginate | |
def render_paging(request, pages, page_obj, count, limit): | |
pages = sample(pages, request.page) | |
get = request.GET.copy() | |
get.pop('page', None) | |
return render_to_string('pagination.html', { | |
'path': request.path_info.split('/')[-1].split('.')[0], | |
'mobile': bool(getattr(request, 'mobile', 0)), | |
'pages': pages, | |
'page_obj': page_obj, | |
'page_count': count//limit, | |
'is_paginated': count > limit, | |
'getvars': '&' + get.urlencode() if get else '' | |
}) | |
def sample(pages, current): | |
"""Show first few, few around the current page & a last page""" | |
if len(pages) > 20: | |
ls = [] | |
prev = False | |
for x in pages: | |
a = False | |
if x in range(1, 5)\ | |
or x in range(current-5, current+5)\ | |
or x == pages[-1]: | |
a = x | |
if prev or a: | |
ls.append(a) | |
prev = a | |
pages = ls | |
return pages | |
# Example pagination.html | |
""" | |
{% if is_paginated %} | |
<ul class="pagination"> | |
{% if page_obj.has_previous %} | |
<li><a href="{{ path }}{% if page_obj.previous_page_number != 1 %}.{{ page_obj.previous_page_number }}{% endif %}"> | |
<small>◀</small></a> | |
{% endif %} | |
{% for page in pages %} | |
{% if page %} | |
{% ifequal page page_obj.number %} | |
<li class="active"><a href="#">{{ page }}</a> | |
{% else %} | |
<li><a href="{{ path }}{% if page != 1 %}.{{ page }}{% endif %}">{{ page }}</a> | |
{% endifequal %} | |
{% else %} | |
… | |
{% endif %} | |
{% endfor %} | |
{% if page_obj.has_next %} | |
<li><a href="{{ path }}.{{ page_obj.next_page_number }}"><small>▶</small></a> | |
{% endif %} | |
</ul> | |
{% endif %} | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment