|
1
|
"""Shared pagination helpers used across all list views.""" |
|
2
|
|
|
3
|
import math |
|
4
|
|
|
5
|
PER_PAGE_OPTIONS = [25, 50, 100] |
|
6
|
|
|
7
|
|
|
8
|
def get_per_page(request, default=25): |
|
9
|
"""Get per_page from request, constrained to PER_PAGE_OPTIONS.""" |
|
10
|
try: |
|
11
|
per_page = int(request.GET.get("per_page", default)) |
|
12
|
except (ValueError, TypeError): |
|
13
|
per_page = default |
|
14
|
return per_page if per_page in PER_PAGE_OPTIONS else default |
|
15
|
|
|
16
|
|
|
17
|
def manual_paginate(items, request, per_page=None): |
|
18
|
"""Paginate a plain list and return (sliced_items, pagination_dict). |
|
19
|
|
|
20
|
The pagination dict has keys compatible with the _pagination_manual.html partial: |
|
21
|
has_previous, has_next, previous_page_number, next_page_number, number, num_pages, count. |
|
22
|
""" |
|
23
|
if per_page is None: |
|
24
|
per_page = get_per_page(request) |
|
25
|
total = len(items) |
|
26
|
num_pages = max(1, math.ceil(total / per_page)) |
|
27
|
try: |
|
28
|
page = int(request.GET.get("page", 1)) |
|
29
|
except (ValueError, TypeError): |
|
30
|
page = 1 |
|
31
|
page = max(1, min(page, num_pages)) |
|
32
|
offset = (page - 1) * per_page |
|
33
|
sliced = items[offset : offset + per_page] |
|
34
|
pagination = { |
|
35
|
"has_previous": page > 1, |
|
36
|
"has_next": offset + per_page < total, |
|
37
|
"previous_page_number": page - 1, |
|
38
|
"next_page_number": page + 1, |
|
39
|
"number": page, |
|
40
|
"num_pages": num_pages, |
|
41
|
"count": total, |
|
42
|
} |
|
43
|
return sliced, pagination |
|
44
|
|