Skip to content

Commit be0078f

Browse files
authored
Merge pull request #94 from CristopherH95/post-pagination
Fix issues with POST pagination
2 parents 103efb0 + 9d7d87f commit be0078f

File tree

2 files changed

+123
-9
lines changed

2 files changed

+123
-9
lines changed

rest_framework_datatables/pagination.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,43 @@ def get_count_and_total_count(self, queryset, view):
4343

4444

4545
class DatatablesPageNumberPagination(DatatablesMixin, PageNumberPagination):
46+
def get_page_size(self, request):
47+
if self.page_size_query_param:
48+
try:
49+
size = int(get_param(request, self.page_size_query_param))
50+
if size <= 0:
51+
raise ValueError()
52+
if self.max_page_size is not None:
53+
return min(size, self.max_page_size)
54+
return size
55+
except (ValueError, TypeError):
56+
pass
57+
return self.page_size
58+
59+
def get_page(self, request, page_size):
60+
try:
61+
start = int(get_param(request, self.page_query_param, 0))
62+
return int(start / page_size) + 1
63+
except ValueError:
64+
return None
65+
4666
def paginate_queryset(self, queryset, request, view=None):
4767
if request.accepted_renderer.format != 'datatables':
4868
self.is_datatable_request = False
4969
return super(
5070
DatatablesPageNumberPagination, self
5171
).paginate_queryset(queryset, request, view)
5272

53-
length = get_param(request, 'length')
73+
self.page_query_param = 'start'
74+
self.page_size_query_param = 'length'
75+
length = get_param(request, self.page_size_query_param)
5476

5577
if length is None or length == '-1':
5678
return None
5779
self.count, self.total_count = self.get_count_and_total_count(
5880
queryset, view
5981
)
6082
self.is_datatable_request = True
61-
self.page_size_query_param = 'length'
6283
page_size = self.get_page_size(request)
6384
if not page_size: # pragma: no cover
6485
return None
@@ -73,8 +94,7 @@ def count(self):
7394
return self.value
7495

7596
paginator = CachedCountPaginator(self.count, queryset, page_size)
76-
start = int(get_param(request, 'start', 0))
77-
page_number = int(start / page_size) + 1
97+
page_number = self.get_page(request, page_size)
7898

7999
try:
80100
self.page = paginator.page(page_number)
@@ -97,7 +117,7 @@ def get_limit(self, request):
97117
if self.max_limit is not None:
98118
return min(limit_value, self.max_limit)
99119
return limit_value
100-
except ValueError:
120+
except (ValueError, TypeError):
101121
return self.default_limit
102122

103123
def get_offset(self, request):
@@ -107,16 +127,16 @@ def get_offset(self, request):
107127
raise ValueError
108128

109129
return offset_value
110-
except ValueError:
130+
except (ValueError, TypeError):
111131
return 0
112132

113133
def paginate_queryset(self, queryset, request, view=None):
114134
if request.accepted_renderer.format == 'datatables':
115135
self.is_datatable_request = True
116-
if get_param(request, 'length') is None:
117-
return None
118136
self.limit_query_param = 'length'
119137
self.offset_query_param = 'start'
138+
if get_param(request, self.limit_query_param) is None:
139+
return None
120140
self.count, self.total_count = self.get_count_and_total_count(
121141
queryset, view
122142
)

tests/test_api.py

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ def post(self, request, *args, **kwargs):
2323
return self.list(request, *args, **kwargs)
2424

2525

26+
class DatatablesPageNumberPaginationCustomMax(DatatablesPageNumberPagination):
27+
max_page_size = 15
28+
29+
30+
class DatatablesLimitOffsetPaginationCustomMax(DatatablesLimitOffsetPagination):
31+
max_limit = 15
32+
33+
2634
class TestApiTestCase(TestCase):
2735
fixtures = ['test_data']
2836

@@ -82,10 +90,36 @@ def test_pagenumber_pagination_show_all(self):
8290
result = response.json()
8391
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], len(result['data']), result['data'][0]['artist_name']), expected)
8492

85-
def test_pagenumber_pagination_invalid_page(self):
93+
def test_pagenumber_pagination_invalid_page1(self):
8694
response = self.client.get('/api/albums/?format=datatables&length=10&start=20&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
8795
self.assertEquals(response.status_code, 404)
8896

97+
def test_pagenumber_pagination_invalid_page2(self):
98+
response = self.client.get('/api/albums/?format=datatables&length=10&start=abc&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
99+
self.assertEquals(response.status_code, 404)
100+
101+
def test_pagenumber_pagination_invalid_page_size1(self):
102+
response = self.client.get('/api/albums/?format=datatables&length=abc&start=10&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
103+
expected = (15, 15, 5, 'Elvis Presley')
104+
result = response.json()
105+
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], len(result['data']), result['data'][0]['artist_name']), expected)
106+
107+
def test_pagenumber_pagination_invalid_page_size2(self):
108+
response = self.client.get('/api/albums/?format=datatables&length=-999&start=10&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
109+
expected = (15, 15, 5, 'Elvis Presley')
110+
result = response.json()
111+
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], len(result['data']), result['data'][0]['artist_name']), expected)
112+
113+
@override_settings(REST_FRAMEWORK={
114+
'DEFAULT_PAGINATION_CLASS': __name__ + '.DatatablesPageNumberPaginationCustomMax',
115+
})
116+
def test_pagenumber_pagination_invalid_max(self):
117+
AlbumViewSet.pagination_class = DatatablesPageNumberPaginationCustomMax
118+
response = self.client.get('/api/albums/?format=datatables&length=20&start=10&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
119+
expected = (15, 15, 15, 'The Beatles')
120+
result = response.json()
121+
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], len(result['data']), result['data'][0]['artist_name']), expected)
122+
89123
@override_settings(REST_FRAMEWORK={
90124
'DEFAULT_PAGINATION_CLASS': 'rest_framework_datatables.pagination.DatatablesLimitOffsetPagination',
91125
})
@@ -108,6 +142,66 @@ def test_limitoffset_pagination_no_length(self):
108142
result = response.json()
109143
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], result['data'][0]['artist_name']), expected)
110144

145+
@override_settings(REST_FRAMEWORK={
146+
'DEFAULT_PAGINATION_CLASS': 'rest_framework_datatables.pagination.DatatablesLimitOffsetPagination',
147+
})
148+
def test_limitoffset_pagination_invalid_length1(self):
149+
AlbumViewSet.pagination_class = DatatablesLimitOffsetPagination
150+
client = APIClient()
151+
response = client.get(
152+
'/api/albums/?format=datatables&length=abc&start=10&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
153+
expected = (15, 15, 'Elvis Presley')
154+
result = response.json()
155+
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], result['data'][0]['artist_name']), expected)
156+
157+
@override_settings(REST_FRAMEWORK={
158+
'DEFAULT_PAGINATION_CLASS': 'rest_framework_datatables.pagination.DatatablesLimitOffsetPagination',
159+
})
160+
def test_limitoffset_pagination_invalid_length2(self):
161+
AlbumViewSet.pagination_class = DatatablesLimitOffsetPagination
162+
client = APIClient()
163+
response = client.get(
164+
'/api/albums/?format=datatables&length=-999&start=10&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
165+
expected = (15, 15, 'Elvis Presley')
166+
result = response.json()
167+
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], result['data'][0]['artist_name']), expected)
168+
169+
@override_settings(REST_FRAMEWORK={
170+
'DEFAULT_PAGINATION_CLASS': 'rest_framework_datatables.pagination.DatatablesLimitOffsetPagination',
171+
})
172+
def test_limitoffset_pagination_invalid_start1(self):
173+
AlbumViewSet.pagination_class = DatatablesLimitOffsetPagination
174+
client = APIClient()
175+
response = client.get(
176+
'/api/albums/?format=datatables&length=10&start=abc&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
177+
expected = (15, 15, 'The Beatles')
178+
result = response.json()
179+
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], result['data'][0]['artist_name']), expected)
180+
181+
@override_settings(REST_FRAMEWORK={
182+
'DEFAULT_PAGINATION_CLASS': 'rest_framework_datatables.pagination.DatatablesLimitOffsetPagination',
183+
})
184+
def test_limitoffset_pagination_invalid_start2(self):
185+
AlbumViewSet.pagination_class = DatatablesLimitOffsetPagination
186+
client = APIClient()
187+
response = client.get(
188+
'/api/albums/?format=datatables&length=10&start=-999&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
189+
expected = (15, 15, 'The Beatles')
190+
result = response.json()
191+
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], result['data'][0]['artist_name']), expected)
192+
193+
@override_settings(REST_FRAMEWORK={
194+
'DEFAULT_PAGINATION_CLASS': __name__ + '.DatatablesLimitOffsetPaginationCustomMax',
195+
})
196+
def test_limitoffset_pagination_invalid_max(self):
197+
AlbumViewSet.pagination_class = DatatablesLimitOffsetPaginationCustomMax
198+
client = APIClient()
199+
response = client.get(
200+
'/api/albums/?format=datatables&length=20&start=10&columns[0][data]=name&columns[1][data]=artist_name&draw=1')
201+
expected = (15, 15, 'Elvis Presley')
202+
result = response.json()
203+
self.assertEquals((result['recordsFiltered'], result['recordsTotal'], result['data'][0]['artist_name']), expected)
204+
111205
@override_settings(REST_FRAMEWORK={
112206
'DEFAULT_PAGINATION_CLASS': 'rest_framework_datatables.pagination.DatatablesLimitOffsetPagination',
113207
})

0 commit comments

Comments
 (0)