mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-05-10 02:43:41 +03:00
Polishing to page size query parameters & more docs
This commit is contained in:
parent
9973cf329a
commit
31f01bd631
|
@ -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.
|
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
|
## MultipleObjectAPIView
|
||||||
|
|
||||||
Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [MultipleObjectMixin].
|
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].
|
**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
|
## SingleObjectAPIView
|
||||||
|
|
||||||
Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [SingleObjectMixin].
|
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].
|
**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
|
# Mixins
|
||||||
|
@ -147,10 +165,6 @@ Provides a `.list(request, *args, **kwargs)` method, that implements listing a q
|
||||||
|
|
||||||
Should be mixed in with [MultipleObjectAPIView].
|
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
|
## CreateModelMixin
|
||||||
|
|
||||||
Provides a `.create(request, *args, **kwargs)` method, that implements creating and saving a new model instance.
|
Provides a `.create(request, *args, **kwargs)` method, that implements creating and saving a new model instance.
|
||||||
|
|
|
@ -96,11 +96,21 @@ Default: `rest_framework.serializers.ModelSerializer`
|
||||||
|
|
||||||
Default: `rest_framework.pagination.PaginationSerializer`
|
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
|
## UNAUTHENTICATED_USER
|
||||||
|
|
||||||
|
@ -150,14 +160,10 @@ Default: `'accept'`
|
||||||
|
|
||||||
Default: `'format'`
|
Default: `'format'`
|
||||||
|
|
||||||
## PAGE_SIZE_KWARG
|
## FORMAT_SUFFIX_KWARG
|
||||||
|
|
||||||
Allows you to globally pass a page size parameter for an individual request.
|
**TODO**
|
||||||
|
|
||||||
The name of the GET parameter of views which inherit ListModelMixin for requesting data with an individual page size.
|
Default: `'format'`
|
||||||
|
|
||||||
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/
|
[cite]: http://www.python.org/dev/peps/pep-0020/
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
## Master
|
## Master
|
||||||
|
|
||||||
* Support for `read_only_fields` on `ModelSerializer` classes.
|
* 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
|
## 2.1.2
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ class GenericAPIView(views.APIView):
|
||||||
"""
|
"""
|
||||||
Base class for all other generic views.
|
Base class for all other generic views.
|
||||||
"""
|
"""
|
||||||
|
model = None
|
||||||
serializer_class = None
|
serializer_class = None
|
||||||
model_serializer_class = api_settings.DEFAULT_MODEL_SERIALIZER_CLASS
|
model_serializer_class = api_settings.DEFAULT_MODEL_SERIALIZER_CLASS
|
||||||
|
|
||||||
|
@ -30,8 +31,10 @@ class GenericAPIView(views.APIView):
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
"""
|
"""
|
||||||
Return the class to use for the serializer.
|
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
|
serializer_class = self.serializer_class
|
||||||
|
|
||||||
|
@ -58,29 +61,42 @@ class MultipleObjectAPIView(MultipleObjectMixin, GenericAPIView):
|
||||||
|
|
||||||
pagination_serializer_class = api_settings.DEFAULT_PAGINATION_SERIALIZER_CLASS
|
pagination_serializer_class = api_settings.DEFAULT_PAGINATION_SERIALIZER_CLASS
|
||||||
paginate_by = api_settings.PAGINATE_BY
|
paginate_by = api_settings.PAGINATE_BY
|
||||||
|
paginate_by_param = api_settings.PAGINATE_BY_PARAM
|
||||||
filter_backend = api_settings.FILTER_BACKEND
|
filter_backend = api_settings.FILTER_BACKEND
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
def filter_queryset(self, queryset):
|
||||||
|
"""
|
||||||
|
Given a queryset, filter it with whichever filter backend is in use.
|
||||||
|
"""
|
||||||
if not self.filter_backend:
|
if not self.filter_backend:
|
||||||
return queryset
|
return queryset
|
||||||
backend = self.filter_backend()
|
backend = self.filter_backend()
|
||||||
return backend.filter_queryset(self.request, queryset, self)
|
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 SerializerClass(self.pagination_serializer_class):
|
||||||
class Meta:
|
class Meta:
|
||||||
object_serializer_class = self.get_serializer_class()
|
object_serializer_class = self.get_serializer_class()
|
||||||
|
|
||||||
return SerializerClass
|
pagination_serializer_class = SerializerClass
|
||||||
|
|
||||||
def get_pagination_serializer(self, page=None):
|
|
||||||
pagination_serializer_class = self.get_pagination_serializer_class()
|
|
||||||
context = self.get_serializer_context()
|
context = self.get_serializer_context()
|
||||||
return pagination_serializer_class(instance=page, context=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):
|
class SingleObjectAPIView(SingleObjectMixin, GenericAPIView):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -7,7 +7,6 @@ which allows mixin classes to be composed in interesting ways.
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.settings import api_settings
|
|
||||||
|
|
||||||
|
|
||||||
class CreateModelMixin(object):
|
class CreateModelMixin(object):
|
||||||
|
@ -40,7 +39,6 @@ class ListModelMixin(object):
|
||||||
Should be mixed in with `MultipleObjectAPIView`.
|
Should be mixed in with `MultipleObjectAPIView`.
|
||||||
"""
|
"""
|
||||||
empty_error = u"Empty list and '%(class_name)s.allow_empty' is False."
|
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):
|
def list(self, request, *args, **kwargs):
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
|
@ -66,17 +64,6 @@ class ListModelMixin(object):
|
||||||
|
|
||||||
return Response(serializer.data)
|
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):
|
class RetrieveModelMixin(object):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -54,12 +54,19 @@ DEFAULTS = {
|
||||||
'user': None,
|
'user': None,
|
||||||
'anon': None,
|
'anon': None,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
# Pagination
|
||||||
'PAGINATE_BY': None,
|
'PAGINATE_BY': None,
|
||||||
|
'PAGINATE_BY_PARAM': None,
|
||||||
|
|
||||||
|
# Filtering
|
||||||
'FILTER_BACKEND': None,
|
'FILTER_BACKEND': None,
|
||||||
|
|
||||||
|
# Authentication
|
||||||
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
|
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
|
||||||
'UNAUTHENTICATED_TOKEN': None,
|
'UNAUTHENTICATED_TOKEN': None,
|
||||||
|
|
||||||
|
# Browser enhancements
|
||||||
'FORM_METHOD_OVERRIDE': '_method',
|
'FORM_METHOD_OVERRIDE': '_method',
|
||||||
'FORM_CONTENT_OVERRIDE': '_content',
|
'FORM_CONTENT_OVERRIDE': '_content',
|
||||||
'FORM_CONTENTTYPE_OVERRIDE': '_content_type',
|
'FORM_CONTENTTYPE_OVERRIDE': '_content_type',
|
||||||
|
@ -67,8 +74,6 @@ DEFAULTS = {
|
||||||
'URL_FORMAT_OVERRIDE': 'format',
|
'URL_FORMAT_OVERRIDE': 'format',
|
||||||
|
|
||||||
'FORMAT_SUFFIX_KWARG': 'format',
|
'FORMAT_SUFFIX_KWARG': 'format',
|
||||||
|
|
||||||
'PAGE_SIZE_KWARG': 'page_size'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,25 +36,17 @@ if django_filters:
|
||||||
|
|
||||||
class DefaultPageSizeKwargView(generics.ListAPIView):
|
class DefaultPageSizeKwargView(generics.ListAPIView):
|
||||||
"""
|
"""
|
||||||
View for testing default page_size usage
|
View for testing default paginate_by_param usage
|
||||||
"""
|
"""
|
||||||
model = BasicModel
|
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
|
model = BasicModel
|
||||||
page_size_kwarg = 'ps'
|
paginate_by_param = 'page_size'
|
||||||
|
|
||||||
|
|
||||||
class NonePageSizeKwargView(generics.ListAPIView):
|
|
||||||
"""
|
|
||||||
View for testing None page_size usage
|
|
||||||
"""
|
|
||||||
model = BasicModel
|
|
||||||
page_size_kwarg = None
|
|
||||||
|
|
||||||
|
|
||||||
class IntegrationTestPagination(TestCase):
|
class IntegrationTestPagination(TestCase):
|
||||||
|
@ -181,9 +173,9 @@ class UnitTestPagination(TestCase):
|
||||||
self.assertEquals(serializer.data['results'], self.objects[20:])
|
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):
|
def setUp(self):
|
||||||
|
@ -199,26 +191,17 @@ class TestDefaultPageSizeKwarg(TestCase):
|
||||||
]
|
]
|
||||||
self.view = DefaultPageSizeKwargView.as_view()
|
self.view = DefaultPageSizeKwargView.as_view()
|
||||||
|
|
||||||
def test_default_page_size(self):
|
def test_unpaginated(self):
|
||||||
"""
|
"""
|
||||||
Tests the default page size for this view.
|
Tests the default page size for this view.
|
||||||
no page size --> no limit --> no meta data
|
no page size --> no limit --> no meta data
|
||||||
"""
|
"""
|
||||||
request = factory.get('/')
|
request = factory.get('/')
|
||||||
response = self.view(request).render()
|
response = self.view(request)
|
||||||
self.assertEquals(response.data, self.data)
|
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 TestCustomPaginateByParam(TestCase):
|
||||||
class TestCustomPageSizeKwarg(TestCase):
|
|
||||||
"""
|
"""
|
||||||
Tests for list views with default page size kwarg
|
Tests for list views with default page size kwarg
|
||||||
"""
|
"""
|
||||||
|
@ -234,7 +217,7 @@ class TestCustomPageSizeKwarg(TestCase):
|
||||||
{'id': obj.id, 'text': obj.text}
|
{'id': obj.id, 'text': obj.text}
|
||||||
for obj in self.objects.all()
|
for obj in self.objects.all()
|
||||||
]
|
]
|
||||||
self.view = CustomPageSizeKwargView.as_view()
|
self.view = PaginateByParamView.as_view()
|
||||||
|
|
||||||
def test_default_page_size(self):
|
def test_default_page_size(self):
|
||||||
"""
|
"""
|
||||||
|
@ -245,55 +228,11 @@ class TestCustomPageSizeKwarg(TestCase):
|
||||||
response = self.view(request).render()
|
response = self.view(request).render()
|
||||||
self.assertEquals(response.data, self.data)
|
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')
|
request = factory.get('/?page_size=5')
|
||||||
response = self.view(request).render()
|
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['count'], 13)
|
||||||
self.assertEquals(response.data['results'], self.data[:5])
|
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)
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user