From 3806af3d15dcbf9c5e1e390d1ae3808f12191342 Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Fri, 13 Nov 2015 16:02:19 +0100 Subject: [PATCH] allow setting a custom Django Paginator in pagination.PageNumberPagination --- docs/api-guide/pagination.md | 1 + rest_framework/pagination.py | 4 ++- tests/test_pagination.py | 59 ++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 881fbf14..da94a1a8 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -95,6 +95,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 fc7b9096..60aa3b74 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -168,6 +168,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' @@ -194,7 +196,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`.