From d3cd2074117b62f638e2159776868d91a304b5b4 Mon Sep 17 00:00:00 2001 From: homm Date: Mon, 13 Jul 2015 18:48:19 +0300 Subject: [PATCH 1/2] make offset_cutoff customizable --- rest_framework/pagination.py | 14 +++++++------- tests/test_pagination.py | 12 +++++++++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index bea10d036..d8793f693 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -454,6 +454,12 @@ class CursorPagination(BasePagination): ordering = '-created' template = 'rest_framework/pagination/previous_and_next.html' + # The offset in the cursor is used in situations where we have a + # nearly-unique index. (Eg millisecond precision creation timestamps) + # We guard against malicious users attempting to cause expensive database + # queries, by having a hard cap on the maximum possible size of the offset. + offset_cutoff = 1000 + def paginate_queryset(self, queryset, request, view=None): if self.page_size is None: return None @@ -675,18 +681,12 @@ class CursorPagination(BasePagination): if encoded is None: return None - # The offset in the cursor is used in situations where we have a - # nearly-unique index. (Eg millisecond precision creation timestamps) - # We guard against malicious users attempting to cause expensive database - # queries, by having a hard cap on the maximum possible size of the offset. - OFFSET_CUTOFF = 1000 - try: querystring = b64decode(encoded.encode('ascii')).decode('ascii') tokens = urlparse.parse_qs(querystring, keep_blank_values=True) offset = tokens.get('o', ['0'])[0] - offset = _positive_int(offset, cutoff=OFFSET_CUTOFF) + offset = _positive_int(offset, cutoff=self.offset_cutoff) reverse = tokens.get('r', ['0'])[0] reverse = bool(int(reverse)) diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 03c4fdf47..74f090a52 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -6,7 +6,7 @@ import pytest from rest_framework import ( exceptions, filters, generics, pagination, serializers, status ) -from rest_framework.pagination import PAGE_BREAK, PageLink +from rest_framework.pagination import PAGE_BREAK, PageLink, Cursor from rest_framework.request import Request from rest_framework.test import APIRequestFactory @@ -648,6 +648,16 @@ class TestCursorPagination: assert isinstance(self.pagination.to_html(), type('')) + def test_offset_cutoff(self): + (previous, current, next, previous_url, next_url) = self.get_pages('/') + + next_url = self.pagination.encode_cursor(Cursor(1050, False, None)) + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [7, 7, 7, 8, 9] + assert current == [] + assert next is None + def test_get_displayed_page_numbers(): """ From f91d332c6c3fc53cfcabdf4323e57755fad7dd13 Mon Sep 17 00:00:00 2001 From: homm Date: Mon, 13 Jul 2015 18:50:25 +0300 Subject: [PATCH 2/2] use position from page only when page is exist --- rest_framework/pagination.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index d8793f693..7fe8559c5 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -540,7 +540,7 @@ class CursorPagination(BasePagination): if not self.has_next: return None - if self.cursor and self.cursor.reverse and self.cursor.offset != 0: + if self.cursor and self.cursor.reverse and self.cursor.offset != 0 and self.page: # If we're reversing direction and we have an offset cursor # then we cannot use the first position we find as a marker. compare = self._get_position_from_instance(self.page[-1], self.ordering) @@ -588,7 +588,7 @@ class CursorPagination(BasePagination): if not self.has_previous: return None - if self.cursor and not self.cursor.reverse and self.cursor.offset != 0: + if self.cursor and not self.cursor.reverse and self.cursor.offset != 0 and self.page: # If we're reversing direction and we have an offset cursor # then we cannot use the first position we find as a marker. compare = self._get_position_from_instance(self.page[0], self.ordering)