diff options
| author | Tom Christie | 2012-11-16 22:45:57 +0000 | 
|---|---|---|
| committer | Tom Christie | 2012-11-16 22:45:57 +0000 | 
| commit | 31f01bd6315f46bf28bb4c9c25a5298785fc4fc6 (patch) | |
| tree | 8f2c0ef593402a23118d71c7f81734c30ef5d2cc | |
| parent | 9973cf329a2133a900256b53236348ef3c870842 (diff) | |
| download | django-rest-framework-31f01bd6315f46bf28bb4c9c25a5298785fc4fc6.tar.bz2 | |
Polishing to page size query parameters & more docs
| -rw-r--r-- | docs/api-guide/generic-views.md | 22 | ||||
| -rw-r--r-- | docs/api-guide/settings.md | 26 | ||||
| -rw-r--r-- | docs/topics/release-notes.md | 3 | ||||
| -rw-r--r-- | rest_framework/generics.py | 32 | ||||
| -rw-r--r-- | rest_framework/mixins.py | 13 | ||||
| -rw-r--r-- | rest_framework/settings.py | 9 | ||||
| -rw-r--r-- | rest_framework/tests/pagination.py | 85 | 
7 files changed, 79 insertions, 111 deletions
| diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 3346c70a..33ec89d2 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -123,18 +123,36 @@ Each of the generic views provided is built by combining one of the base views b  Extends REST framework's `APIView` class, adding support for serialization of model instances and model querysets. +**Attributes**: + +* `model` - The model that should be used for this view.  Used as a fallback for determining the serializer if `serializer_class` is not set, and as a fallback for determining the queryset if `queryset` is not set.  Otherwise not required. +* `serializer_class` - The serializer class that should be used for validating and deserializing input, and for serializing output.  If unset, this defaults to creating a serializer class using `self.model`, with the `DEFAULT_MODEL_SERIALIZER_CLASS` setting as the base serializer class. +  ## MultipleObjectAPIView  Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [MultipleObjectMixin].  **See also:** ccbv.co.uk documentation for [MultipleObjectMixin][multiple-object-mixin-classy]. +**Attributes**: + +* `queryset` - The queryset that should be used for returning objects from this view.  If unset, defaults to the default queryset manager for `self.model`. +* `paginate_by` - The size of pages to use with paginated data.  If set to `None` then pagination is turned off.  If unset this uses the same value as the `PAGINATE_BY` setting, which defaults to `None`. +* `paginate_by_param` - The name of a query parameter, which can be used by the client to overide the default page size to use for pagination.  If unset this uses the same value as the `PAGINATE_BY_PARAM` setting, which defaults to `None`. +  ## SingleObjectAPIView  Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [SingleObjectMixin].  **See also:** ccbv.co.uk documentation for [SingleObjectMixin][single-object-mixin-classy]. +**Attributes**: + +* `queryset` - The queryset that should be used when retrieving an object from this view.  If unset, defaults to the default queryset manager for `self.model`. +* `pk_kwarg` - The URL kwarg that should be used to look up objects by primary key. Defaults to `'pk'`. [Can only be set to non-default on Django 1.4+] +* `slug_kwarg` - The URL kwarg that should be used to look up objects by a slug. Defaults to `'slug'`.  [Can only be set to non-default on Django 1.4+] +* `slug_field` - The field on the model that should be used to look up objects by a slug.  If used, this should typically be set to a field with `unique=True`. Defaults to `'slug'`. +  ---  # Mixins @@ -147,10 +165,6 @@ Provides a `.list(request, *args, **kwargs)` method, that implements listing a q  Should be mixed in with [MultipleObjectAPIView]. -**Arguments**: - -* `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  Provides a `.create(request, *args, **kwargs)` method, that implements creating and saving a new model instance. diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 8fce9e4e..7884d096 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -96,11 +96,21 @@ Default: `rest_framework.serializers.ModelSerializer`  Default: `rest_framework.pagination.PaginationSerializer` -## FORMAT_SUFFIX_KWARG +## FILTER_BACKEND -**TODO** +The filter backend class that should be used for generic filtering.  If set to `None` then generic filtering is disabled. -Default: `'format'` +## PAGINATE_BY + +The default page size to use for pagination.  If set to `None`, pagination is disabled by default. + +Default: `None` + +## PAGINATE_BY_KWARG + +The name of a query parameter, which can be used by the client to overide the default page size to use for pagination.  If set to `None`, clients may not override the default page size. + +Default: `None`  ## UNAUTHENTICATED_USER @@ -150,14 +160,10 @@ Default: `'accept'`  Default: `'format'` -## PAGE_SIZE_KWARG - -Allows you to globally pass a page size parameter for an individual request. - -The name of the GET parameter of views which inherit ListModelMixin for requesting data with an individual page size. +## FORMAT_SUFFIX_KWARG -If the value if this setting is `None` the passing a page size is turned off by default. +**TODO** -Default: `'page_size'` +Default: `'format'`  [cite]: http://www.python.org/dev/peps/pep-0020/ diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 85c19f5b..869cabc8 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -7,7 +7,8 @@  ## Master  * Support for `read_only_fields` on `ModelSerializer` classes. -* Support for individual page sizes per request via `page_size` GET parameter in views which inherit ListModelMixin. +* Support for clients overriding the pagination page sizes.  Use the `PAGINATE_BY_PARAM` setting or set the `paginate_by_param` attribute on a generic view. +* 201 Responses now return a 'Location' header.  ## 2.1.2 diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 9ad03f71..dcf4dfd9 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -14,6 +14,7 @@ class GenericAPIView(views.APIView):      """      Base class for all other generic views.      """ +    model = None      serializer_class = None      model_serializer_class = api_settings.DEFAULT_MODEL_SERIALIZER_CLASS @@ -30,8 +31,10 @@ class GenericAPIView(views.APIView):      def get_serializer_class(self):          """          Return the class to use for the serializer. -        Use `self.serializer_class`, falling back to constructing a -        model serializer class from `self.model_serializer_class` + +        Defaults to using `self.serializer_class`, falls back to constructing a +        model serializer class using `self.model_serializer_class`, with +        `self.model` as the model.          """          serializer_class = self.serializer_class @@ -58,29 +61,42 @@ class MultipleObjectAPIView(MultipleObjectMixin, GenericAPIView):      pagination_serializer_class = api_settings.DEFAULT_PAGINATION_SERIALIZER_CLASS      paginate_by = api_settings.PAGINATE_BY +    paginate_by_param = api_settings.PAGINATE_BY_PARAM      filter_backend = api_settings.FILTER_BACKEND      def filter_queryset(self, queryset): +        """ +        Given a queryset, filter it with whichever filter backend is in use. +        """          if not self.filter_backend:              return queryset          backend = self.filter_backend()          return backend.filter_queryset(self.request, queryset, self) -    def get_pagination_serializer_class(self): +    def get_pagination_serializer(self, page=None):          """ -        Return the class to use for the pagination serializer. +        Return a serializer instance to use with paginated data.          """          class SerializerClass(self.pagination_serializer_class):              class Meta:                  object_serializer_class = self.get_serializer_class() -        return SerializerClass - -    def get_pagination_serializer(self, page=None): -        pagination_serializer_class = self.get_pagination_serializer_class() +        pagination_serializer_class = SerializerClass          context = self.get_serializer_context()          return pagination_serializer_class(instance=page, context=context) +    def get_paginate_by(self, queryset): +        """ +        Return the size of pages to use with pagination. +        """ +        if self.paginate_by_param: +            params = self.request.QUERY_PARAMS +            try: +                return int(params[self.paginate_by_param]) +            except (KeyError, ValueError): +                pass +        return self.paginate_by +  class SingleObjectAPIView(SingleObjectMixin, GenericAPIView):      """ diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 0da4c2cc..53c4d984 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -7,7 +7,6 @@ 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): @@ -40,7 +39,6 @@ class ListModelMixin(object):      Should be mixed in with `MultipleObjectAPIView`.      """      empty_error = u"Empty list and '%(class_name)s.allow_empty' is False." -    page_size_kwarg = api_settings.PAGE_SIZE_KWARG      def list(self, request, *args, **kwargs):          queryset = self.get_queryset() @@ -66,17 +64,6 @@ class ListModelMixin(object):          return Response(serializer.data) -    def get_paginate_by(self, queryset): -        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_kwarg) -                    return page_size -                except ValueError: -                    pass -        return super(ListModelMixin, self).get_paginate_by(queryset) -  class RetrieveModelMixin(object):      """ diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 8883b963..ee24a4ad 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -54,12 +54,19 @@ DEFAULTS = {          'user': None,          'anon': None,      }, + +    # Pagination      'PAGINATE_BY': None, +    'PAGINATE_BY_PARAM': None, + +    # Filtering      'FILTER_BACKEND': None, +    # Authentication      'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',      'UNAUTHENTICATED_TOKEN': None, +    # Browser enhancements      'FORM_METHOD_OVERRIDE': '_method',      'FORM_CONTENT_OVERRIDE': '_content',      'FORM_CONTENTTYPE_OVERRIDE': '_content_type', @@ -67,8 +74,6 @@ DEFAULTS = {      'URL_FORMAT_OVERRIDE': 'format',      'FORMAT_SUFFIX_KWARG': 'format', - -    'PAGE_SIZE_KWARG': 'page_size'  } diff --git a/rest_framework/tests/pagination.py b/rest_framework/tests/pagination.py index 8aae2147..3062007d 100644 --- a/rest_framework/tests/pagination.py +++ b/rest_framework/tests/pagination.py @@ -36,25 +36,17 @@ if django_filters:  class DefaultPageSizeKwargView(generics.ListAPIView):      """ -    View for testing default page_size usage +    View for testing default paginate_by_param usage      """      model = BasicModel -class CustomPageSizeKwargView(generics.ListAPIView): +class PaginateByParamView(generics.ListAPIView):      """ -    View for testing custom page_size usage +    View for testing custom paginate_by_param usage      """      model = BasicModel -    page_size_kwarg = 'ps' - - -class NonePageSizeKwargView(generics.ListAPIView): -    """ -    View for testing None page_size usage -    """ -    model = BasicModel -    page_size_kwarg = None +    paginate_by_param = 'page_size'  class IntegrationTestPagination(TestCase): @@ -181,9 +173,9 @@ class UnitTestPagination(TestCase):          self.assertEquals(serializer.data['results'], self.objects[20:]) -class TestDefaultPageSizeKwarg(TestCase): +class TestUnpaginated(TestCase):      """ -    Tests for list views with default page size kwarg +    Tests for list views without pagination.      """      def setUp(self): @@ -199,26 +191,17 @@ class TestDefaultPageSizeKwarg(TestCase):          ]          self.view = DefaultPageSizeKwargView.as_view() -    def test_default_page_size(self): +    def test_unpaginated(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() +        response = self.view(request)          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): +class TestCustomPaginateByParam(TestCase):      """      Tests for list views with default page size kwarg      """ @@ -234,7 +217,7 @@ class TestCustomPageSizeKwarg(TestCase):          {'id': obj.id, 'text': obj.text}          for obj in self.objects.all()          ] -        self.view = CustomPageSizeKwargView.as_view() +        self.view = PaginateByParamView.as_view()      def test_default_page_size(self):          """ @@ -245,55 +228,11 @@ class TestCustomPageSizeKwarg(TestCase):          response = self.view(request).render()          self.assertEquals(response.data, self.data) -    def test_disabled_default_page_size_kwarg(self): +    def test_paginate_by_param(self):          """ -        If page_size_kwarg is set set, the default page_size kwarg should not work. +        If paginate_by_param is set, the new kwarg should limit per view requests.          """          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) | 
