diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 778076c5..da096bde 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -97,6 +97,7 @@ The `PageNumberPagination` class includes a number of attributes that may be ove To set these attributes you should override the `PageNumberPagination` class, and then enable your custom pagination class as above. +* `django_paginator_class` - The Django Paginator class to use. Default is `django.core.paginator.Paginator`, which should be fine for most usecases. * `page_size` - A numeric value indicating the page size. If set, this overrides the `PAGE_SIZE` setting. Defaults to the same value as the `PAGE_SIZE` settings key. * `page_query_param` - A string value indicating the name of the query parameter to use for the pagination control. * `page_size_query_param` - If set, this is a string value indicating the name of a query parameter that allows the client to set the page size on a per-request basis. Defaults to `None`, indicating that the client may not control the requested page size. diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 7f9c135f..1051dc5b 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -169,6 +169,8 @@ class PageNumberPagination(BasePagination): # Defaults to `None`, meaning pagination is disabled. page_size = api_settings.PAGE_SIZE + django_paginator_class = DjangoPaginator + # Client can control the page using this query parameter. page_query_param = 'page' @@ -195,7 +197,7 @@ class PageNumberPagination(BasePagination): if not page_size: return None - paginator = DjangoPaginator(queryset, page_size) + paginator = self.django_paginator_class(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 diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 2e608567..c6caaf64 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import pytest +from django.core.paginator import Paginator as DjangoPaginator from rest_framework import ( exceptions, filters, generics, pagination, serializers, status @@ -249,6 +250,64 @@ class TestPageNumberPagination: self.paginate_queryset(request) +class TestPageNumberPaginationOverride: + """ + Unit tests for `pagination.PageNumberPagination`. + + the Django Paginator Class is overridden. + """ + + def setup(self): + class OverriddenDjangoPaginator(DjangoPaginator): + # override the count in our overriden Django Paginator + # we will only return one page, with one item + count = 1 + + class ExamplePagination(pagination.PageNumberPagination): + django_paginator_class = OverriddenDjangoPaginator + page_size = 5 + + self.pagination = ExamplePagination() + self.queryset = range(1, 101) + + def paginate_queryset(self, request): + return list(self.pagination.paginate_queryset(self.queryset, request)) + + def get_paginated_content(self, queryset): + response = self.pagination.get_paginated_response(queryset) + return response.data + + def get_html_context(self): + return self.pagination.get_html_context() + + def test_no_page_number(self): + request = Request(factory.get('/')) + queryset = self.paginate_queryset(request) + content = self.get_paginated_content(queryset) + context = self.get_html_context() + assert queryset == [1] + assert content == { + 'results': [1, ], + 'previous': None, + 'next': None, + 'count': 1 + } + assert context == { + 'previous_url': None, + 'next_url': None, + 'page_links': [ + PageLink('http://testserver/', 1, True, False), + ] + } + assert not self.pagination.display_page_controls + assert isinstance(self.pagination.to_html(), type('')) + + def test_invalid_page(self): + request = Request(factory.get('/', {'page': 'invalid'})) + with pytest.raises(exceptions.NotFound): + self.paginate_queryset(request) + + class TestLimitOffset: """ Unit tests for `pagination.LimitOffsetPagination`.