123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- """
- Generic views that provide commonly needed behaviour.
- """
- from django.core.exceptions import ValidationError
- 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 rest_framework import mixins, views
- from rest_framework.settings import api_settings
- def get_object_or_404(queryset, *filter_args, **filter_kwargs):
- """
- Same as Django's standard shortcut, but make sure to also raise 404
- if the filter_kwargs don't match the required types.
- """
- try:
- return _get_object_or_404(queryset, *filter_args, **filter_kwargs)
- except (TypeError, ValueError, ValidationError):
- raise Http404
- 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
- # `get_queryset()` instead of accessing the `queryset` property directly,
- # as `queryset` will get evaluated only once, and those results are cached
- # for all subsequent requests.
- queryset = None
- serializer_class = None
- # 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
- # The filter backend classes to use for queryset filtering
- filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
- # The style to use for queryset pagination.
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
- def get_queryset(self):
- """
- Get the list of items for this view.
- This must be an iterable, and may be a queryset.
- Defaults to using `self.queryset`.
- This method should always be used rather than accessing `self.queryset`
- directly, as `self.queryset` gets evaluated only once, and those results
- are cached for all subsequent requests.
- You may want to override this if you need to provide different
- querysets depending on the incoming request.
- (Eg. return a list of items that is specific to the user)
- """
- assert self.queryset is not None, (
- "'%s' should either include a `queryset` attribute, "
- "or override the `get_queryset()` method."
- % self.__class__.__name__
- )
- queryset = self.queryset
- if isinstance(queryset, QuerySet):
- # Ensure queryset is re-evaluated on each request.
- queryset = queryset.all()
- return queryset
- def get_object(self):
- """
- Returns the object the view is displaying.
- You may want to override this if you need to provide non-standard
- queryset lookups. Eg if objects are referenced using multiple
- keyword arguments in the url conf.
- """
- queryset = self.filter_queryset(self.get_queryset())
- # Perform the lookup filtering.
- lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
- assert lookup_url_kwarg in self.kwargs, (
- 'Expected view %s to be called with a URL keyword argument '
- 'named "%s". Fix your URL conf, or set the `.lookup_field` '
- 'attribute on the view correctly.' %
- (self.__class__.__name__, lookup_url_kwarg)
- )
- filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
- obj = get_object_or_404(queryset, **filter_kwargs)
- # May raise a permission denied
- self.check_object_permissions(self.request, obj)
- 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.setdefault('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 paginator(self):
- """
- The paginator instance associated with the view, or `None`.
- """
- if not hasattr(self, '_paginator'):
- if self.pagination_class is None:
- self._paginator = None
- else:
- self._paginator = self.pagination_class()
- return self._paginator
- def paginate_queryset(self, queryset):
- """
- Return a single page of results, or `None` if pagination is disabled.
- """
- if self.paginator is None:
- return None
- return self.paginator.paginate_queryset(queryset, self.request, view=self)
- def get_paginated_response(self, data):
- """
- Return a paginated style `Response` object for the given output data.
- """
- assert self.paginator is not None
- return self.paginator.get_paginated_response(data)
- # Concrete view classes that provide method handlers
- # by composing the mixin classes with the base view.
- class CreateAPIView(mixins.CreateModelMixin,
- GenericAPIView):
- """
- Concrete view for creating a model instance.
- """
- def post(self, request, *args, **kwargs):
- return self.create(request, *args, **kwargs)
- class ListAPIView(mixins.ListModelMixin,
- GenericAPIView):
- """
- Concrete view for listing a queryset.
- """
- def get(self, request, *args, **kwargs):
- return self.list(request, *args, **kwargs)
- class RetrieveAPIView(mixins.RetrieveModelMixin,
- GenericAPIView):
- """
- Concrete view for retrieving a model instance.
- """
- def get(self, request, *args, **kwargs):
- return self.retrieve(request, *args, **kwargs)
- class DestroyAPIView(mixins.DestroyModelMixin,
- GenericAPIView):
- """
- Concrete view for deleting a model instance.
- """
- def delete(self, request, *args, **kwargs):
- return self.destroy(request, *args, **kwargs)
- class UpdateAPIView(mixins.UpdateModelMixin,
- GenericAPIView):
- """
- Concrete view for updating a model instance.
- """
- def put(self, request, *args, **kwargs):
- return self.update(request, *args, **kwargs)
- def patch(self, request, *args, **kwargs):
- return self.partial_update(request, *args, **kwargs)
- class ListCreateAPIView(mixins.ListModelMixin,
- mixins.CreateModelMixin,
- GenericAPIView):
- """
- Concrete view for listing a queryset or creating a model instance.
- """
- def get(self, request, *args, **kwargs):
- return self.list(request, *args, **kwargs)
- def post(self, request, *args, **kwargs):
- return self.create(request, *args, **kwargs)
- class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
- mixins.UpdateModelMixin,
- GenericAPIView):
- """
- Concrete view for retrieving, updating a model instance.
- """
- def get(self, request, *args, **kwargs):
- return self.retrieve(request, *args, **kwargs)
- def put(self, request, *args, **kwargs):
- return self.update(request, *args, **kwargs)
- def patch(self, request, *args, **kwargs):
- return self.partial_update(request, *args, **kwargs)
- class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
- mixins.DestroyModelMixin,
- GenericAPIView):
- """
- Concrete view for retrieving or deleting a model instance.
- """
- def get(self, request, *args, **kwargs):
- return self.retrieve(request, *args, **kwargs)
- def delete(self, request, *args, **kwargs):
- return self.destroy(request, *args, **kwargs)
- class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
- mixins.UpdateModelMixin,
- mixins.DestroyModelMixin,
- GenericAPIView):
- """
- Concrete view for retrieving, updating or deleting a model instance.
- """
- def get(self, request, *args, **kwargs):
- return self.retrieve(request, *args, **kwargs)
- def put(self, request, *args, **kwargs):
- return self.update(request, *args, **kwargs)
- def patch(self, request, *args, **kwargs):
- return self.partial_update(request, *args, **kwargs)
- def delete(self, request, *args, **kwargs):
- return self.destroy(request, *args, **kwargs)
|