mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-04-25 11:33:43 +03:00
Pagination: allowing negative page numbers and offsets
This commit is contained in:
parent
d3dd45b3f4
commit
16dc419ff0
|
@ -191,6 +191,7 @@ class PageNumberPagination(BasePagination):
|
||||||
last_page_strings = ('last',)
|
last_page_strings = ('last',)
|
||||||
|
|
||||||
template = 'rest_framework/pagination/numbers.html'
|
template = 'rest_framework/pagination/numbers.html'
|
||||||
|
allow_negative_page_numbers = False
|
||||||
|
|
||||||
invalid_page_message = _('Invalid page.')
|
invalid_page_message = _('Invalid page.')
|
||||||
|
|
||||||
|
@ -225,6 +226,14 @@ class PageNumberPagination(BasePagination):
|
||||||
page_number = request.query_params.get(self.page_query_param) or 1
|
page_number = request.query_params.get(self.page_query_param) or 1
|
||||||
if page_number in self.last_page_strings:
|
if page_number in self.last_page_strings:
|
||||||
page_number = paginator.num_pages
|
page_number = paginator.num_pages
|
||||||
|
if self.allow_negative_page_numbers:
|
||||||
|
try:
|
||||||
|
page_number = int(page_number)
|
||||||
|
if page_number < 0:
|
||||||
|
page_number = paginator.num_pages + page_number
|
||||||
|
return max(page_number, 0)
|
||||||
|
except ValueError:
|
||||||
|
return page_number
|
||||||
return page_number
|
return page_number
|
||||||
|
|
||||||
def get_paginated_response(self, data):
|
def get_paginated_response(self, data):
|
||||||
|
@ -384,6 +393,7 @@ class LimitOffsetPagination(BasePagination):
|
||||||
offset_query_description = _('The initial index from which to return the results.')
|
offset_query_description = _('The initial index from which to return the results.')
|
||||||
max_limit = None
|
max_limit = None
|
||||||
template = 'rest_framework/pagination/numbers.html'
|
template = 'rest_framework/pagination/numbers.html'
|
||||||
|
allow_negative_offsets = False
|
||||||
|
|
||||||
def paginate_queryset(self, queryset, request, view=None):
|
def paginate_queryset(self, queryset, request, view=None):
|
||||||
self.request = request
|
self.request = request
|
||||||
|
@ -447,6 +457,11 @@ class LimitOffsetPagination(BasePagination):
|
||||||
|
|
||||||
def get_offset(self, request):
|
def get_offset(self, request):
|
||||||
try:
|
try:
|
||||||
|
if self.allow_negative_offsets:
|
||||||
|
offset = int(request.query_params[self.offset_query_param])
|
||||||
|
if offset < 0:
|
||||||
|
offset = self.count + offset
|
||||||
|
return max(offset, 0)
|
||||||
return _positive_int(
|
return _positive_int(
|
||||||
request.query_params[self.offset_query_param],
|
request.query_params[self.offset_query_param],
|
||||||
)
|
)
|
||||||
|
|
|
@ -260,6 +260,40 @@ class TestPageNumberPagination:
|
||||||
with pytest.raises(exceptions.NotFound):
|
with pytest.raises(exceptions.NotFound):
|
||||||
self.paginate_queryset(request)
|
self.paginate_queryset(request)
|
||||||
|
|
||||||
|
def test_negative_page(self):
|
||||||
|
request = Request(factory.get('/', {'page': -1}))
|
||||||
|
print(request)
|
||||||
|
with pytest.raises(exceptions.NotFound):
|
||||||
|
self.paginate_queryset(request)
|
||||||
|
|
||||||
|
def test_allowed_negative_page(self):
|
||||||
|
self.pagination.allow_negative_page_numbers = True
|
||||||
|
request = Request(factory.get('/', {'page': -2}))
|
||||||
|
queryset = self.paginate_queryset(request)
|
||||||
|
content = self.get_paginated_content(queryset)
|
||||||
|
context = self.get_html_context()
|
||||||
|
assert queryset == [86, 87, 88, 89, 90]
|
||||||
|
assert content == {
|
||||||
|
'results': [86, 87, 88, 89, 90],
|
||||||
|
'previous': 'http://testserver/?page=17',
|
||||||
|
'next': 'http://testserver/?page=19',
|
||||||
|
'count': 100
|
||||||
|
}
|
||||||
|
assert context == {
|
||||||
|
'previous_url': 'http://testserver/?page=17',
|
||||||
|
'next_url': 'http://testserver/?page=19',
|
||||||
|
'page_links': [
|
||||||
|
PageLink('http://testserver/', 1, False, False),
|
||||||
|
PAGE_BREAK,
|
||||||
|
PageLink('http://testserver/?page=17', 17, False, False),
|
||||||
|
PageLink('http://testserver/?page=18', 18, True, False),
|
||||||
|
PageLink('http://testserver/?page=19', 19, False, False),
|
||||||
|
PageLink('http://testserver/?page=20', 20, False, False),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
assert self.pagination.display_page_controls
|
||||||
|
assert isinstance(self.pagination.to_html(), str)
|
||||||
|
|
||||||
def test_get_paginated_response_schema(self):
|
def test_get_paginated_response_schema(self):
|
||||||
unpaginated_schema = {
|
unpaginated_schema = {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
|
@ -527,6 +561,42 @@ class TestLimitOffset:
|
||||||
queryset = self.paginate_queryset(request)
|
queryset = self.paginate_queryset(request)
|
||||||
assert queryset == [1, 2, 3, 4, 5]
|
assert queryset == [1, 2, 3, 4, 5]
|
||||||
|
|
||||||
|
def test_negative_offset(self):
|
||||||
|
"""
|
||||||
|
A negative offset query param should be treated as 0.
|
||||||
|
"""
|
||||||
|
request = Request(factory.get('/', {'limit': 5, 'offset': -5}))
|
||||||
|
queryset = self.paginate_queryset(request)
|
||||||
|
assert queryset == [1, 2, 3, 4, 5]
|
||||||
|
|
||||||
|
def test_allowed_negative_offset(self):
|
||||||
|
"""
|
||||||
|
A negative offset query param should be treated as `count - offset`.
|
||||||
|
"""
|
||||||
|
self.pagination.allow_negative_offsets = True
|
||||||
|
request = Request(factory.get('/', {'limit': 5, 'offset': -10}))
|
||||||
|
queryset = self.paginate_queryset(request)
|
||||||
|
content = self.get_paginated_content(queryset)
|
||||||
|
context = self.get_html_context()
|
||||||
|
assert queryset == [91, 92, 93, 94, 95]
|
||||||
|
assert content == {
|
||||||
|
'results': [91, 92, 93, 94, 95],
|
||||||
|
'previous': 'http://testserver/?limit=5&offset=85',
|
||||||
|
'next': 'http://testserver/?limit=5&offset=95',
|
||||||
|
'count': 100
|
||||||
|
}
|
||||||
|
assert context == {
|
||||||
|
'previous_url': 'http://testserver/?limit=5&offset=85',
|
||||||
|
'next_url': 'http://testserver/?limit=5&offset=95',
|
||||||
|
'page_links': [
|
||||||
|
PageLink('http://testserver/?limit=5', 1, False, False),
|
||||||
|
PAGE_BREAK,
|
||||||
|
PageLink('http://testserver/?limit=5&offset=85', 18, False, False),
|
||||||
|
PageLink('http://testserver/?limit=5&offset=90', 19, True, False),
|
||||||
|
PageLink('http://testserver/?limit=5&offset=95', 20, False, False),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
def test_invalid_limit(self):
|
def test_invalid_limit(self):
|
||||||
"""
|
"""
|
||||||
An invalid limit query param should be ignored in favor of the default.
|
An invalid limit query param should be ignored in favor of the default.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user