mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-04-24 19:12:10 +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',)
|
||||
|
||||
template = 'rest_framework/pagination/numbers.html'
|
||||
allow_negative_page_numbers = False
|
||||
|
||||
invalid_page_message = _('Invalid page.')
|
||||
|
||||
|
@ -225,6 +226,14 @@ class PageNumberPagination(BasePagination):
|
|||
page_number = request.query_params.get(self.page_query_param) or 1
|
||||
if page_number in self.last_page_strings:
|
||||
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
|
||||
|
||||
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.')
|
||||
max_limit = None
|
||||
template = 'rest_framework/pagination/numbers.html'
|
||||
allow_negative_offsets = False
|
||||
|
||||
def paginate_queryset(self, queryset, request, view=None):
|
||||
self.request = request
|
||||
|
@ -447,6 +457,11 @@ class LimitOffsetPagination(BasePagination):
|
|||
|
||||
def get_offset(self, request):
|
||||
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(
|
||||
request.query_params[self.offset_query_param],
|
||||
)
|
||||
|
|
|
@ -260,6 +260,40 @@ class TestPageNumberPagination:
|
|||
with pytest.raises(exceptions.NotFound):
|
||||
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):
|
||||
unpaginated_schema = {
|
||||
'type': 'object',
|
||||
|
@ -527,6 +561,42 @@ class TestLimitOffset:
|
|||
queryset = self.paginate_queryset(request)
|
||||
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):
|
||||
"""
|
||||
An invalid limit query param should be ignored in favor of the default.
|
||||
|
|
Loading…
Reference in New Issue
Block a user