CenterForOpenScience/scrapi

View on GitHub
api/webview/pagination.py

Summary

Maintainability
A
0 mins
Test Coverage
import six
from collections import OrderedDict

from django.core.paginator import Paginator as DjangoPaginator
from django.core.paginator import InvalidPage, PageNotAnInteger, EmptyPage

from rest_framework.response import Response
from rest_framework.exceptions import NotFound
from rest_framework.utils.urls import replace_query_param
from rest_framework.pagination import PageNumberPagination


class PageNumberPaginationWithoutCount(PageNumberPagination):
    # Set any other options you want here like page_size

    def get_paginated_response(self, data):
        return Response(OrderedDict([
            ('next', self.get_next_link() if data else None),
            ('previous', self.get_previous_link()),
            ('results', data)
        ]))

    def get_next_link(self):
        url = self.request.build_absolute_uri()
        page_number = self.page.next_page_number()
        return replace_query_param(url, self.page_query_param, page_number)

    def paginate_queryset(self, queryset, request, view=None):
        """
        Paginate a queryset if required, either returning a
        page object, or `None` if pagination is not configured for this view.
        """
        page_size = self.get_page_size(request)
        if not page_size:
            return None

        paginator = PaginatorWithoutCount(queryset, page_size)
        page_number = request.query_params.get(self.page_query_param, 1)
        if page_number in self.last_page_strings:
            page_number = paginator.num_pages

        try:
            self.page = paginator.page(page_number)
        except InvalidPage as exc:
            msg = self.invalid_page_message.format(
                page_number=page_number, message=six.text_type(exc)
            )
            raise NotFound(msg)

        if paginator.num_pages > 1 and self.template is not None:
            # The browsable API should display pagination controls.
            self.display_page_controls = True

        self.request = request
        return list(self.page)


class PaginatorWithoutCount(DjangoPaginator):

    def __init__(self, *args, **kwargs):
        super(PaginatorWithoutCount, self).__init__(*args, **kwargs)
        self._count = 0

    def page(self, number):
        number = self.validate_number(number)
        bottom = (number - 1) * self.per_page
        top = bottom + self.per_page
        return self._get_page(self.object_list[bottom:top], number, self)

    def validate_number(self, number):
        try:
            number = int(number)
        except (TypeError, ValueError):
            raise PageNotAnInteger('That page number is not an integer')
        if number < 1:
            raise EmptyPage('That page number is less than 1')
        return number