From 023b065ddc08735c487adff76cc62a864efe1697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Wed, 14 Nov 2012 16:02:50 +0100 Subject: [PATCH 1/7] added support for passing page_size per request --- rest_framework/mixins.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index c3625a88e..f725fc9e9 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -55,6 +55,16 @@ class ListModelMixin(object): return Response(serializer.data) + def get_paginate_by(self, queryset): + page_size_param = self.request.QUERY_PARAMS.get('page_size') + if page_size_param: + try: + page_size = int(page_size_param) + return page_size + except ValueError: + pass + return super(ListModelMixin, self).get_paginate_by(queryset) + class RetrieveModelMixin(object): """ From 33fe0d2bea5ce391a69047236c121ec5b33d9b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Wed, 14 Nov 2012 16:08:14 +0100 Subject: [PATCH 2/7] added release note --- docs/topics/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 35e8a8b35..40ce43521 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -7,6 +7,7 @@ ## Master * Support for `read_only_fields` on `ModelSerializer` classes. +* Support for `page_size` GET parameter in view which inherit ListModelMixin ## 2.1.2 From 5967f15f7f5c87987ab60e4b7dc682b06f3ab511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Wed, 14 Nov 2012 16:11:35 +0100 Subject: [PATCH 3/7] updated docs --- docs/api-guide/generic-views.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 360ef1a2e..9e5119cbd 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -147,6 +147,10 @@ Provides a `.list(request, *args, **kwargs)` method, that implements listing a q Should be mixed in with [MultipleObjectAPIView]. +**Arguments**: + +* `page_size` - Hook to adjust page_size per request. + ## CreateModelMixin Provides a `.create(request, *args, **kwargs)` method, that implements creating and saving a new model instance. From 38e94bb8b4e04249b18b9b57ef2ddcb7cfc4efa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Thu, 15 Nov 2012 11:15:05 +0100 Subject: [PATCH 4/7] added global and per resource on/off switch + updated docs --- docs/api-guide/generic-views.md | 3 ++- docs/api-guide/settings.md | 6 ++++++ rest_framework/mixins.py | 18 +++++++++++------- rest_framework/settings.py | 4 +++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 9e5119cbd..734a91e92 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -149,7 +149,8 @@ Should be mixed in with [MultipleObjectAPIView]. **Arguments**: -* `page_size` - Hook to adjust page_size per request. +* `allow_page_size_param` - Allows you to overwrite the global settings `ALLOW_PAGE_SIZE_PARAM` for a specific view. +* `page_size_param` - Allows you to customize the page_size parameter. Default is `page_size`. ## CreateModelMixin diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 4f87b30da..2f90369b4 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -150,4 +150,10 @@ Default: `'accept'` Default: `'format'` +## ALLOW_PAGE_SIZE_PARAM + +Allows you to globally pass a page size parameter for an individual request. + +Default: `'True'` + [cite]: http://www.python.org/dev/peps/pep-0020/ diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index f725fc9e9..d64e7e56a 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -7,6 +7,7 @@ which allows mixin classes to be composed in interesting ways. from django.http import Http404 from rest_framework import status from rest_framework.response import Response +from rest_framework.settings import api_settings class CreateModelMixin(object): @@ -32,6 +33,8 @@ class ListModelMixin(object): Should be mixed in with `MultipleObjectAPIView`. """ empty_error = u"Empty list and '%(class_name)s.allow_empty' is False." + allow_page_size_param = api_settings.ALLOW_PAGE_SIZE_PARAM + page_size_param = 'page_size' def list(self, request, *args, **kwargs): self.object_list = self.get_filtered_queryset() @@ -56,13 +59,14 @@ class ListModelMixin(object): return Response(serializer.data) def get_paginate_by(self, queryset): - page_size_param = self.request.QUERY_PARAMS.get('page_size') - if page_size_param: - try: - page_size = int(page_size_param) - return page_size - except ValueError: - pass + if self.allow_page_size_param: + page_size_param = self.request.QUERY_PARAMS.get(self.page_size_param) + if page_size_param: + try: + page_size = int(page_size_param) + return page_size + except ValueError: + pass return super(ListModelMixin, self).get_paginate_by(queryset) diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 906a7cf6c..1daa9dfdb 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -66,7 +66,9 @@ DEFAULTS = { 'URL_ACCEPT_OVERRIDE': 'accept', 'URL_FORMAT_OVERRIDE': 'format', - 'FORMAT_SUFFIX_KWARG': 'format' + 'FORMAT_SUFFIX_KWARG': 'format', + + 'ALLOW_PAGE_SIZE_PARAM': True } From b17a9818008cf3828adb896ae9be134fb63c5693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Thu, 15 Nov 2012 11:24:17 +0100 Subject: [PATCH 5/7] updated release noted for page_size stuff --- docs/topics/release-notes.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 40ce43521..f4a76c899 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -7,7 +7,9 @@ ## Master * Support for `read_only_fields` on `ModelSerializer` classes. -* Support for `page_size` GET parameter in view which inherit ListModelMixin +* Support for `page_size` GET parameter in views which inherit ListModelMixin. +* Support for customizing `page_size` param via `page_size_param` attribute. +* Support for allowing `page_size` param globally (via `ALLOW_PAGE_SIZE_PARAM`) and for individual views (via `allow_page_size_param`) ## 2.1.2 From 3ae203a0184d27318a8a828ce322b151ade0340f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Thu, 15 Nov 2012 12:06:43 +0100 Subject: [PATCH 6/7] updated script to just use page_size_kwarg --- docs/api-guide/generic-views.md | 3 +-- docs/api-guide/settings.md | 8 ++++++-- docs/topics/release-notes.md | 4 +--- rest_framework/mixins.py | 11 +++++------ rest_framework/settings.py | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 734a91e92..3346c70a3 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -149,8 +149,7 @@ Should be mixed in with [MultipleObjectAPIView]. **Arguments**: -* `allow_page_size_param` - Allows you to overwrite the global settings `ALLOW_PAGE_SIZE_PARAM` for a specific view. -* `page_size_param` - Allows you to customize the page_size parameter. Default is `page_size`. +* `page_size_kwarg` - Allows you to overwrite the global settings `PAGE_SIZE_KWARG` for a specific view. You can also turn it off for a specific view by setting it to `None`. Default is `page_size`. ## CreateModelMixin diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 2f90369b4..8fce9e4e7 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -150,10 +150,14 @@ Default: `'accept'` Default: `'format'` -## ALLOW_PAGE_SIZE_PARAM +## PAGE_SIZE_KWARG Allows you to globally pass a page size parameter for an individual request. -Default: `'True'` +The name of the GET parameter of views which inherit ListModelMixin for requesting data with an individual page size. + +If the value if this setting is `None` the passing a page size is turned off by default. + +Default: `'page_size'` [cite]: http://www.python.org/dev/peps/pep-0020/ diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index f4a76c899..85c19f5bc 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -7,9 +7,7 @@ ## Master * Support for `read_only_fields` on `ModelSerializer` classes. -* Support for `page_size` GET parameter in views which inherit ListModelMixin. -* Support for customizing `page_size` param via `page_size_param` attribute. -* Support for allowing `page_size` param globally (via `ALLOW_PAGE_SIZE_PARAM`) and for individual views (via `allow_page_size_param`) +* Support for individual page sizes per request via `page_size` GET parameter in views which inherit ListModelMixin. ## 2.1.2 diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index d64e7e56a..d85e0bfbb 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -33,8 +33,7 @@ class ListModelMixin(object): Should be mixed in with `MultipleObjectAPIView`. """ empty_error = u"Empty list and '%(class_name)s.allow_empty' is False." - allow_page_size_param = api_settings.ALLOW_PAGE_SIZE_PARAM - page_size_param = 'page_size' + page_size_kwarg = api_settings.PAGE_SIZE_KWARG def list(self, request, *args, **kwargs): self.object_list = self.get_filtered_queryset() @@ -59,11 +58,11 @@ class ListModelMixin(object): return Response(serializer.data) def get_paginate_by(self, queryset): - if self.allow_page_size_param: - page_size_param = self.request.QUERY_PARAMS.get(self.page_size_param) - if page_size_param: + if self.page_size_kwarg is not None: + page_size_kwarg = self.request.QUERY_PARAMS.get(self.page_size_kwarg) + if page_size_kwarg: try: - page_size = int(page_size_param) + page_size = int(page_size_kwarg) return page_size except ValueError: pass diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 1daa9dfdb..c7b0643fb 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -68,7 +68,7 @@ DEFAULTS = { 'FORMAT_SUFFIX_KWARG': 'format', - 'ALLOW_PAGE_SIZE_PARAM': True + 'PAGE_SIZE_KWARG': 'page_size' } From a701a21587a69ed959533cbcfdaa9c63337c3ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Gro=C3=9F?= Date: Thu, 15 Nov 2012 14:35:34 +0100 Subject: [PATCH 7/7] added page_size_kwarg tests --- rest_framework/tests/pagination.py | 143 ++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/rest_framework/tests/pagination.py b/rest_framework/tests/pagination.py index 713a7255b..8aae21471 100644 --- a/rest_framework/tests/pagination.py +++ b/rest_framework/tests/pagination.py @@ -34,6 +34,29 @@ if django_filters: filter_backend = filters.DjangoFilterBackend +class DefaultPageSizeKwargView(generics.ListAPIView): + """ + View for testing default page_size usage + """ + model = BasicModel + + +class CustomPageSizeKwargView(generics.ListAPIView): + """ + View for testing custom page_size usage + """ + model = BasicModel + page_size_kwarg = 'ps' + + +class NonePageSizeKwargView(generics.ListAPIView): + """ + View for testing None page_size usage + """ + model = BasicModel + page_size_kwarg = None + + class IntegrationTestPagination(TestCase): """ Integration tests for paginated list views. @@ -135,7 +158,7 @@ class IntegrationTestPaginationAndFiltering(TestCase): class UnitTestPagination(TestCase): """ - Unit tests for pagination of primative objects. + Unit tests for pagination of primitive objects. """ def setUp(self): @@ -156,3 +179,121 @@ class UnitTestPagination(TestCase): self.assertEquals(serializer.data['next'], None) self.assertEquals(serializer.data['previous'], '?page=2') self.assertEquals(serializer.data['results'], self.objects[20:]) + + +class TestDefaultPageSizeKwarg(TestCase): + """ + Tests for list views with default page size kwarg + """ + + def setUp(self): + """ + Create 13 BasicModel instances. + """ + for i in range(13): + BasicModel(text=i).save() + self.objects = BasicModel.objects + self.data = [ + {'id': obj.id, 'text': obj.text} + for obj in self.objects.all() + ] + self.view = DefaultPageSizeKwargView.as_view() + + def test_default_page_size(self): + """ + Tests the default page size for this view. + no page size --> no limit --> no meta data + """ + request = factory.get('/') + response = self.view(request).render() + self.assertEquals(response.data, self.data) + + def test_default_page_size_kwarg(self): + """ + If page_size_kwarg is set not set, the default page_size kwarg should limit per view requests. + """ + request = factory.get('/?page_size=5') + response = self.view(request).render() + self.assertEquals(response.data['count'], 13) + self.assertEquals(response.data['results'], self.data[:5]) + + +class TestCustomPageSizeKwarg(TestCase): + """ + Tests for list views with default page size kwarg + """ + + def setUp(self): + """ + Create 13 BasicModel instances. + """ + for i in range(13): + BasicModel(text=i).save() + self.objects = BasicModel.objects + self.data = [ + {'id': obj.id, 'text': obj.text} + for obj in self.objects.all() + ] + self.view = CustomPageSizeKwargView.as_view() + + def test_default_page_size(self): + """ + Tests the default page size for this view. + no page size --> no limit --> no meta data + """ + request = factory.get('/') + response = self.view(request).render() + self.assertEquals(response.data, self.data) + + def test_disabled_default_page_size_kwarg(self): + """ + If page_size_kwarg is set set, the default page_size kwarg should not work. + """ + request = factory.get('/?page_size=5') + response = self.view(request).render() + self.assertEquals(response.data, self.data) + + def test_custom_page_size_kwarg(self): + """ + If page_size_kwarg is set set, the new kwarg should limit per view requests. + """ + request = factory.get('/?ps=5') + response = self.view(request).render() + self.assertEquals(response.data['count'], 13) + self.assertEquals(response.data['results'], self.data[:5]) + + +class TestNonePageSizeKwarg(TestCase): + """ + Tests for list views with default page size kwarg + """ + + def setUp(self): + """ + Create 13 BasicModel instances. + """ + for i in range(13): + BasicModel(text=i).save() + self.objects = BasicModel.objects + self.data = [ + {'id': obj.id, 'text': obj.text} + for obj in self.objects.all() + ] + self.view = NonePageSizeKwargView.as_view() + + def test_default_page_size(self): + """ + Tests the default page size for this view. + no page size --> no limit --> no meta data + """ + request = factory.get('/') + response = self.view(request).render() + self.assertEquals(response.data, self.data) + + def test_none_page_size_kwarg(self): + """ + If page_size_kwarg is set to None, custom page_size per request should be disabled. + """ + request = factory.get('/?page_size=5') + response = self.view(request).render() + self.assertEquals(response.data, self.data)