mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-02-27 00:40:40 +03:00
paginator should validate page and provide default
- use the standard paginator.validate_number method rather strict_postive_int. - support optional paginator method, default_page_number, to get the default page number rather than hard-coding it to 1 - this allows supporting non-integer based pagination which can be an important performance tweak on extermely large datasets or high request loads - relatively thorough unit tests of the changes
This commit is contained in:
parent
c3aeb16557
commit
63e6a3b492
|
@ -145,10 +145,18 @@ class GenericAPIView(views.APIView):
|
||||||
allow_empty_first_page=self.allow_empty)
|
allow_empty_first_page=self.allow_empty)
|
||||||
page_kwarg = self.kwargs.get(self.page_kwarg)
|
page_kwarg = self.kwargs.get(self.page_kwarg)
|
||||||
page_query_param = self.request.QUERY_PARAMS.get(self.page_kwarg)
|
page_query_param = self.request.QUERY_PARAMS.get(self.page_kwarg)
|
||||||
page = page_kwarg or page_query_param or 1
|
page = page_kwarg or page_query_param
|
||||||
|
if not page:
|
||||||
|
# we didn't recieve a page
|
||||||
|
if hasattr(paginator, 'default_page_number'):
|
||||||
|
# our paginator has a method that will provide a default
|
||||||
|
page = paginator.default_page_number()
|
||||||
|
else:
|
||||||
|
# fall back on the base default value
|
||||||
|
page = 1
|
||||||
try:
|
try:
|
||||||
page_number = strict_positive_int(page)
|
page_number = paginator.validate_number(page)
|
||||||
except ValueError:
|
except InvalidPage:
|
||||||
if page == 'last':
|
if page == 'last':
|
||||||
page_number = paginator.num_pages
|
page_number = paginator.num_pages
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -430,3 +430,91 @@ class TestCustomPaginationSerializer(TestCase):
|
||||||
'objects': ['john', 'paul']
|
'objects': ['john', 'paul']
|
||||||
}
|
}
|
||||||
self.assertEqual(serializer.data, expected)
|
self.assertEqual(serializer.data, expected)
|
||||||
|
|
||||||
|
|
||||||
|
class NonIntegerPage(object):
|
||||||
|
|
||||||
|
def __init__(self, paginator, object_list, prev_token, token, next_token):
|
||||||
|
self.paginator = paginator
|
||||||
|
self.object_list = object_list
|
||||||
|
self.prev_token = prev_token
|
||||||
|
self.token = token
|
||||||
|
self.next_token = next_token
|
||||||
|
|
||||||
|
def has_next(self):
|
||||||
|
return not not self.next_token
|
||||||
|
|
||||||
|
def next_page_number(self):
|
||||||
|
return self.next_token
|
||||||
|
|
||||||
|
def has_previous(self):
|
||||||
|
return not not self.prev_token
|
||||||
|
|
||||||
|
def previous_page_number(self):
|
||||||
|
return self.prev_token
|
||||||
|
|
||||||
|
|
||||||
|
class NonIntegerPaginator(object):
|
||||||
|
|
||||||
|
def __init__(self, object_list, per_page):
|
||||||
|
self.object_list = object_list
|
||||||
|
self.per_page = per_page
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
# pretend like we don't know how many pages we have
|
||||||
|
return None
|
||||||
|
|
||||||
|
def default_page_token(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def page(self, token=None):
|
||||||
|
if token:
|
||||||
|
try:
|
||||||
|
first = self.object_list.index(token)
|
||||||
|
except ValueError:
|
||||||
|
first = 0
|
||||||
|
else:
|
||||||
|
first = 0
|
||||||
|
n = len(self.object_list)
|
||||||
|
last = min(first + self.per_page, n)
|
||||||
|
prev_token = self.object_list[last - (2 * self.per_page)] if first else None
|
||||||
|
next_token = self.object_list[last] if last < n else None
|
||||||
|
return NonIntegerPage(self, self.object_list[first:last], prev_token, token, next_token)
|
||||||
|
|
||||||
|
|
||||||
|
class TestNonIntegerPagination(TestCase):
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_pagination_serializer(self):
|
||||||
|
objects = ['john', 'paul', 'george', 'ringo']
|
||||||
|
paginator = NonIntegerPaginator(objects, 2)
|
||||||
|
|
||||||
|
request = APIRequestFactory().get('/foobar')
|
||||||
|
serializer = CustomPaginationSerializer(
|
||||||
|
instance=paginator.page(),
|
||||||
|
context={'request': request}
|
||||||
|
)
|
||||||
|
expected = {
|
||||||
|
'links': {
|
||||||
|
'next': 'http://testserver/foobar?page={0}'.format(objects[2]),
|
||||||
|
'prev': None
|
||||||
|
},
|
||||||
|
'total_results': None,
|
||||||
|
'objects': objects[:2]
|
||||||
|
}
|
||||||
|
self.assertEqual(serializer.data, expected)
|
||||||
|
|
||||||
|
request = APIRequestFactory().get('/foobar')
|
||||||
|
serializer = CustomPaginationSerializer(
|
||||||
|
instance=paginator.page('george'),
|
||||||
|
context={'request': request}
|
||||||
|
)
|
||||||
|
expected = {
|
||||||
|
'links': {
|
||||||
|
'next': None,
|
||||||
|
'prev': 'http://testserver/foobar?page={0}'.format(objects[0]),
|
||||||
|
},
|
||||||
|
'total_results': None,
|
||||||
|
'objects': objects[2:]
|
||||||
|
}
|
||||||
|
self.assertEqual(serializer.data, expected)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user