from django.core.paginator import InvalidPage, Paginator as DjangoPaginator from rest_framework.compat import OrderedDict from rest_framework.exceptions import NotFound from rest_framework.pagination import PageNumberPagination from rest_framework.response import Response class BaseApiPaginator(PageNumberPagination): 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. """ paginator = DjangoPaginator( queryset, self.page_size, orphans=self.page_orphans) page_number = request.query_params.get(self.page_query_param, 1) 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.count > 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) def get_meta(self): pagination = { 'pages': self.page.paginator.num_pages, 'first': None, 'previous': None, 'next': None, 'last': None, } if self.page.has_previous(): pagination['first'] = 1 if self.page.previous_page_number() > 1: pagination['previous'] = self.page.previous_page_number() if self.page.has_next(): pagination['last'] = self.page.paginator.num_pages if self.page.next_page_number() < self.page.paginator.num_pages: pagination['next'] = self.page.next_page_number() return OrderedDict([ ('count', self.page.paginator.count), ('pages', pagination['pages']), ('first', pagination['first']), ('previous', pagination['previous']), ('next', pagination['next']), ('last', pagination['last']) ]) def get_paginated_response(self, data): response_data = self.get_meta() response_data['results'] = data return Response(response_data) def ApiPaginator(per_page=None, orphans=0): return type('ApiPaginator', (BaseApiPaginator, ), { 'page_size': per_page, 'page_orphans': orphans })