aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework/generics.py
diff options
context:
space:
mode:
Diffstat (limited to 'rest_framework/generics.py')
-rw-r--r--rest_framework/generics.py220
1 files changed, 70 insertions, 150 deletions
diff --git a/rest_framework/generics.py b/rest_framework/generics.py
index 0d709c37..12fb6413 100644
--- a/rest_framework/generics.py
+++ b/rest_framework/generics.py
@@ -2,29 +2,13 @@
Generic views that provide commonly needed behaviour.
"""
from __future__ import unicode_literals
-
-from django.core.paginator import Paginator, InvalidPage
from django.db.models.query import QuerySet
from django.http import Http404
from django.shortcuts import get_object_or_404 as _get_object_or_404
-from django.utils import six
-from django.utils.translation import ugettext as _
from rest_framework import views, mixins
from rest_framework.settings import api_settings
-def strict_positive_int(integer_string, cutoff=None):
- """
- Cast a string to a strictly positive integer.
- """
- ret = int(integer_string)
- if ret <= 0:
- raise ValueError()
- if cutoff:
- ret = min(ret, cutoff)
- return ret
-
-
def get_object_or_404(queryset, *filter_args, **filter_kwargs):
"""
Same as Django's standard shortcut, but make sure to also raise 404
@@ -40,7 +24,6 @@ class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
"""
-
# You'll need to either set these attributes,
# or override `get_queryset()`/`get_serializer_class()`.
# If you are overriding a view method, it is important that you call
@@ -50,146 +33,16 @@ class GenericAPIView(views.APIView):
queryset = None
serializer_class = None
- # If you want to use object lookups other than pk, set this attribute.
+ # If you want to use object lookups other than pk, set 'lookup_field'.
# For more complex lookup requirements override `get_object()`.
lookup_field = 'pk'
lookup_url_kwarg = None
- # Pagination settings
- paginate_by = api_settings.PAGINATE_BY
- paginate_by_param = api_settings.PAGINATE_BY_PARAM
- max_paginate_by = api_settings.MAX_PAGINATE_BY
- pagination_serializer_class = api_settings.DEFAULT_PAGINATION_SERIALIZER_CLASS
- page_kwarg = 'page'
-
# The filter backend classes to use for queryset filtering
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
- # The following attribute may be subject to change,
- # and should be considered private API.
- paginator_class = Paginator
-
- def get_serializer_context(self):
- """
- Extra context provided to the serializer class.
- """
- return {
- 'request': self.request,
- 'format': self.format_kwarg,
- 'view': self
- }
-
- def get_serializer(self, *args, **kwargs):
- """
- Return the serializer instance that should be used for validating and
- deserializing input, and for serializing output.
- """
- serializer_class = self.get_serializer_class()
- kwargs['context'] = self.get_serializer_context()
- return serializer_class(*args, **kwargs)
-
- def get_pagination_serializer(self, page):
- """
- Return a serializer instance to use with paginated data.
- """
- class SerializerClass(self.pagination_serializer_class):
- class Meta:
- object_serializer_class = self.get_serializer_class()
-
- pagination_serializer_class = SerializerClass
- context = self.get_serializer_context()
- return pagination_serializer_class(instance=page, context=context)
-
- def paginate_queryset(self, queryset):
- """
- Paginate a queryset if required, either returning a page object,
- or `None` if pagination is not configured for this view.
- """
- page_size = self.get_paginate_by()
- if not page_size:
- return None
-
- paginator = self.paginator_class(queryset, page_size)
- page_kwarg = self.kwargs.get(self.page_kwarg)
- page_query_param = self.request.query_params.get(self.page_kwarg)
- page = page_kwarg or page_query_param or 1
- try:
- page_number = paginator.validate_number(page)
- except InvalidPage:
- if page == 'last':
- page_number = paginator.num_pages
- else:
- raise Http404(_('Choose a valid page number. Page numbers must be a whole number, or must be the string "last".'))
-
- try:
- page = paginator.page(page_number)
- except InvalidPage as exc:
- error_format = _('Invalid page "{page_number}": {message}.')
- raise Http404(error_format.format(
- page_number=page_number, message=six.text_type(exc)
- ))
-
- return page
-
- def filter_queryset(self, queryset):
- """
- Given a queryset, filter it with whichever filter backend is in use.
-
- You are unlikely to want to override this method, although you may need
- to call it either from a list view, or from a custom `get_object`
- method if you want to apply the configured filtering backend to the
- default queryset.
- """
- for backend in self.get_filter_backends():
- queryset = backend().filter_queryset(self.request, queryset, self)
- return queryset
-
- def get_filter_backends(self):
- """
- Returns the list of filter backends that this view requires.
- """
- return list(self.filter_backends)
-
- # The following methods provide default implementations
- # that you may want to override for more complex cases.
-
- def get_paginate_by(self):
- """
- Return the size of pages to use with pagination.
-
- If `PAGINATE_BY_PARAM` is set it will attempt to get the page size
- from a named query parameter in the url, eg. ?page_size=100
-
- Otherwise defaults to using `self.paginate_by`.
- """
- if self.paginate_by_param:
- try:
- return strict_positive_int(
- self.request.query_params[self.paginate_by_param],
- cutoff=self.max_paginate_by
- )
- except (KeyError, ValueError):
- pass
-
- return self.paginate_by
-
- def get_serializer_class(self):
- """
- Return the class to use for the serializer.
- Defaults to using `self.serializer_class`.
-
- You may want to override this if you need to provide different
- serializations depending on the incoming request.
-
- (Eg. admins get full serialization, others get basic serialization)
- """
- assert self.serializer_class is not None, (
- "'%s' should either include a `serializer_class` attribute, "
- "or override the `get_serializer_class()` method."
- % self.__class__.__name__
- )
-
- return self.serializer_class
+ # The style to use for queryset pagination.
+ pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
def get_queryset(self):
"""
@@ -246,6 +99,73 @@ class GenericAPIView(views.APIView):
return obj
+ def get_serializer(self, *args, **kwargs):
+ """
+ Return the serializer instance that should be used for validating and
+ deserializing input, and for serializing output.
+ """
+ serializer_class = self.get_serializer_class()
+ kwargs['context'] = self.get_serializer_context()
+ return serializer_class(*args, **kwargs)
+
+ def get_serializer_class(self):
+ """
+ Return the class to use for the serializer.
+ Defaults to using `self.serializer_class`.
+
+ You may want to override this if you need to provide different
+ serializations depending on the incoming request.
+
+ (Eg. admins get full serialization, others get basic serialization)
+ """
+ assert self.serializer_class is not None, (
+ "'%s' should either include a `serializer_class` attribute, "
+ "or override the `get_serializer_class()` method."
+ % self.__class__.__name__
+ )
+
+ return self.serializer_class
+
+ def get_serializer_context(self):
+ """
+ Extra context provided to the serializer class.
+ """
+ return {
+ 'request': self.request,
+ 'format': self.format_kwarg,
+ 'view': self
+ }
+
+ def filter_queryset(self, queryset):
+ """
+ Given a queryset, filter it with whichever filter backend is in use.
+
+ You are unlikely to want to override this method, although you may need
+ to call it either from a list view, or from a custom `get_object`
+ method if you want to apply the configured filtering backend to the
+ default queryset.
+ """
+ for backend in list(self.filter_backends):
+ queryset = backend().filter_queryset(self.request, queryset, self)
+ return queryset
+
+ @property
+ def pager(self):
+ if not hasattr(self, '_pager'):
+ if self.pagination_class is None:
+ self._pager = None
+ else:
+ self._pager = self.pagination_class()
+ return self._pager
+
+ def paginate_queryset(self, queryset):
+ if self.pager is None:
+ return None
+ return self.pager.paginate_queryset(queryset, self.request, view=self)
+
+ def get_paginated_response(self, objects):
+ return self.pager.get_paginated_response(objects)
+
# Concrete view classes that provide method handlers
# by composing the mixin classes with the base view.