|
1 |
| -from collections import namedtuple |
2 |
| -from math import ceil |
3 |
| - |
4 |
| -from django.utils.translation import gettext as _ |
5 |
| -from rest_framework.exceptions import NotFound |
6 |
| -from rest_framework.pagination import PageNumberPagination |
7 |
| - |
8 |
| - |
9 |
| -class PaginatorPage: |
10 |
| - |
11 |
| - """ |
12 |
| - Mimics the result from a paginator. |
13 |
| -
|
14 |
| - By using this class, we avoid having to override a lot of methods |
15 |
| - of `PageNumberPagination` to make it work with the ES DSL object. |
16 |
| - """ |
17 |
| - |
18 |
| - def __init__(self, page_number, total_pages, count): |
19 |
| - self.number = page_number |
20 |
| - Paginator = namedtuple("Paginator", ["num_pages", "count"]) |
21 |
| - self.paginator = Paginator(total_pages, count) |
22 |
| - |
23 |
| - def has_next(self): |
24 |
| - return self.number < self.paginator.num_pages |
25 |
| - |
26 |
| - def has_previous(self): |
27 |
| - return self.number > 1 |
28 |
| - |
29 |
| - def next_page_number(self): |
30 |
| - return self.number + 1 |
31 |
| - |
32 |
| - def previous_page_number(self): |
33 |
| - return self.number - 1 |
34 |
| - |
35 |
| - |
36 |
| -class SearchPagination(PageNumberPagination): |
37 |
| - |
38 |
| - """Paginator for the results of PageSearch.""" |
39 |
| - |
40 |
| - page_size = 50 |
41 |
| - page_size_query_param = "page_size" |
42 |
| - max_page_size = 100 |
43 |
| - |
44 |
| - def _get_page_number(self, number): |
45 |
| - try: |
46 |
| - if isinstance(number, float) and not number.is_integer(): |
47 |
| - raise ValueError |
48 |
| - number = int(number) |
49 |
| - except (TypeError, ValueError): |
50 |
| - number = -1 |
51 |
| - return number |
52 |
| - |
53 |
| - def paginate_queryset(self, queryset, request, view=None): |
54 |
| - """ |
55 |
| - Override to get the paginated result from the ES queryset. |
56 |
| -
|
57 |
| - This makes use of our custom paginator and slicing support from the ES DSL object, |
58 |
| - instead of the one used by django's ORM. |
59 |
| -
|
60 |
| - Mostly inspired by https://github.com/encode/django-rest-framework/blob/acbd9d8222e763c7f9c7dc2de23c430c702e06d4/rest_framework/pagination.py#L191 # noqa |
61 |
| - """ |
62 |
| - # Needed for other methods of this class. |
63 |
| - self.request = request |
64 |
| - |
65 |
| - page_size = self.get_page_size(request) |
66 |
| - page_number = request.query_params.get(self.page_query_param, 1) |
67 |
| - |
68 |
| - original_page_number = page_number |
69 |
| - page_number = self._get_page_number(page_number) |
70 |
| - |
71 |
| - if page_number <= 0: |
72 |
| - msg = self.invalid_page_message.format( |
73 |
| - page_number=original_page_number, |
74 |
| - message=_("Invalid page"), |
75 |
| - ) |
76 |
| - raise NotFound(msg) |
77 |
| - |
78 |
| - start = (page_number - 1) * page_size |
79 |
| - end = page_number * page_size |
80 |
| - |
81 |
| - result = [] |
82 |
| - total_count = 0 |
83 |
| - total_pages = 1 |
84 |
| - |
85 |
| - if queryset: |
86 |
| - result = queryset[start:end].execute() |
87 |
| - total_count = result.hits.total["value"] |
88 |
| - hits = max(1, total_count) |
89 |
| - total_pages = ceil(hits / page_size) |
90 |
| - |
91 |
| - if total_pages > 1 and self.template is not None: |
92 |
| - # The browsable API should display pagination controls. |
93 |
| - self.display_page_controls = True |
94 |
| - |
95 |
| - # Needed for other methods of this class. |
96 |
| - self.page = PaginatorPage( |
97 |
| - page_number=page_number, |
98 |
| - total_pages=total_pages, |
99 |
| - count=total_count, |
100 |
| - ) |
101 |
| - |
102 |
| - return result |
0 commit comments