From 10ed50ced7781658a6bac1f4824f44c25e0cd59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Krzyz=CC=87aniak?= Date: Mon, 11 Jul 2016 16:56:30 +0200 Subject: [PATCH 1/2] Added Limit/Offset pagination without counts. --- rest_framework/pagination.py | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 6ad10d860..abcf64435 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -15,7 +15,9 @@ from django.utils import six from django.utils.six.moves.urllib import parse as urlparse from django.utils.translation import ugettext_lazy as _ +from rest_framework import status from rest_framework.compat import template_render +from rest_framework.exceptions import APIException from rest_framework.exceptions import NotFound from rest_framework.response import Response from rest_framework.settings import api_settings @@ -720,3 +722,67 @@ class CursorPagination(BasePagination): def get_fields(self, view): return [self.cursor_query_param] + + +class IncorrectLimitOffsetError(APIException): + status_code = status.HTTP_400_BAD_REQUEST + default_detail = _('Incorrect offset or limit.') + + +class NoCountsLimitOffsetPagination(LimitOffsetPagination): + """ + A limit/offset based pagination, without performing counts. For example: + + http://api.example.org/accounts/?limit=100 - will return first 100 items + http://api.example.org/accounts/?offset=400&limit=100 - will returns 100 items starting from 401th + http://api.example.org/accounts/?offset=-50&limit=100 - will return first 50 items + + Pros: + - no counts + - easier to use than cursor pagination (especially if you need sorting) + - works with angular ui-scroll (which requires negative offsets) + + Cons: + - html is not handled + - skip is a relatively slow operation, so this paginator is not as fast as cursor paginator when you use + large offsets + """ + def paginate_queryset(self, queryset, request, view=None): + self.limit = self.get_limit(request) + if self.limit is None: + raise IncorrectLimitOffsetError + + self.offset = self.get_offset(request) + if self.offset < 0: + self.limit += self.offset # + because offset is negative + self.offset = 0 + if self.limit <= 0: + raise IncorrectLimitOffsetError + + self.request = request + self.results = list(queryset[self.offset:self.offset + self.limit]) + return self.results + + def get_paginated_response(self, data): + return Response(OrderedDict([ + ('next', self.get_next_link()), + ('previous', self.get_previous_link()), + ('results', data) + ])) + return self.default_limit + + def get_offset(self, request): + try: + return int(request.query_params[self.offset_query_param]) + except (KeyError, ValueError): + return 0 + + def get_next_link(self): + if len(self.results) < self.limit: + return None + + url = self.request.build_absolute_uri() + url = replace_query_param(url, self.limit_query_param, self.limit) + + offset = self.offset + self.limit + return replace_query_param(url, self.offset_query_param, offset) From 33d656e5e83d8c7d3089cbcc1edd0bb0de9274f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Krzyz=CC=87aniak?= Date: Tue, 12 Jul 2016 12:28:59 +0200 Subject: [PATCH 2/2] isort --- rest_framework/pagination.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index abcf64435..0a7351986 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -17,8 +17,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import status from rest_framework.compat import template_render -from rest_framework.exceptions import APIException -from rest_framework.exceptions import NotFound +from rest_framework.exceptions import APIException, NotFound from rest_framework.response import Response from rest_framework.settings import api_settings from rest_framework.utils.urls import remove_query_param, replace_query_param