diff options
Diffstat (limited to 'rest_framework/mixins.py')
| -rw-r--r-- | rest_framework/mixins.py | 66 | 
1 files changed, 40 insertions, 26 deletions
diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 7d9a6e65..f3cd5868 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -10,9 +10,10 @@ from django.http import Http404  from rest_framework import status  from rest_framework.response import Response  from rest_framework.request import clone_request +import warnings -def _get_validation_exclusions(obj, pk=None, slug_field=None): +def _get_validation_exclusions(obj, pk=None, slug_field=None, lookup_field=None):      """      Given a model instance, and an optional pk and slug field,      return the full list of all other field names on that model. @@ -23,21 +24,25 @@ def _get_validation_exclusions(obj, pk=None, slug_field=None):      include = []      if pk: +        # Pending deprecation          pk_field = obj._meta.pk          while pk_field.rel:              pk_field = pk_field.rel.to._meta.pk          include.append(pk_field.name)      if slug_field: +        # Pending deprecation          include.append(slug_field) +    if lookup_field and lookup_field != 'pk': +        include.append(lookup_field) +      return [field.name for field in obj._meta.fields if field.name not in include]  class CreateModelMixin(object):      """      Create a model instance. -    Should be mixed in with any `GenericAPIView`.      """      def create(self, request, *args, **kwargs):          serializer = self.get_serializer(data=request.DATA, files=request.FILES) @@ -62,28 +67,28 @@ class CreateModelMixin(object):  class ListModelMixin(object):      """      List a queryset. -    Should be mixed in with `MultipleObjectAPIView`.      """      empty_error = "Empty list and '%(class_name)s.allow_empty' is False."      def list(self, request, *args, **kwargs): -        queryset = self.get_queryset() -        self.object_list = self.filter_queryset(queryset) +        self.object_list = self.filter_queryset(self.get_queryset())          # Default is to allow empty querysets.  This can be altered by setting          # `.allow_empty = False`, to raise 404 errors on empty querysets. -        allow_empty = self.get_allow_empty() -        if not allow_empty and not self.object_list: +        if not self.allow_empty and not self.object_list: +            warnings.warn( +                'The `allow_empty` parameter is due to be deprecated. ' +                'To use `allow_empty=False` style behavior, You should override ' +                '`get_queryset()` and explicitly raise a 404 on empty querysets.', +                PendingDeprecationWarning +            )              class_name = self.__class__.__name__              error_msg = self.empty_error % {'class_name': class_name}              raise Http404(error_msg) -        # Pagination size is set by the `.paginate_by` attribute, -        # which may be `None` to disable pagination. -        page_size = self.get_paginate_by(self.object_list) -        if page_size: -            packed = self.paginate_queryset(self.object_list, page_size) -            paginator, page, queryset, is_paginated = packed +        # Switch between paginated or standard style responses +        page = self.paginate_queryset(self.object_list) +        if page is not None:              serializer = self.get_pagination_serializer(page)          else:              serializer = self.get_serializer(self.object_list, many=True) @@ -94,12 +99,9 @@ class ListModelMixin(object):  class RetrieveModelMixin(object):      """      Retrieve a model instance. -    Should be mixed in with `SingleObjectAPIView`.      """      def retrieve(self, request, *args, **kwargs): -        queryset = self.get_queryset() -        filtered_queryset = self.filter_queryset(queryset) -        self.object = self.get_object(filtered_queryset) +        self.object = self.get_object()          serializer = self.get_serializer(self.object)          return Response(serializer.data) @@ -107,17 +109,22 @@ class RetrieveModelMixin(object):  class UpdateModelMixin(object):      """      Update a model instance. -    Should be mixed in with `SingleObjectAPIView`.      """ -    def update(self, request, *args, **kwargs): -        partial = kwargs.pop('partial', False) -        self.object = None +    def get_object_or_none(self):          try: -            self.object = self.get_object() +            return self.get_object()          except Http404:              # If this is a PUT-as-create operation, we need to ensure that              # we have relevant permissions, as if this was a POST request. -            self.check_permissions(clone_request(request, 'POST')) +            # This will either raise a PermissionDenied exception, +            # or simply return None +            self.check_permissions(clone_request(self.request, 'POST')) + +    def update(self, request, *args, **kwargs): +        partial = kwargs.pop('partial', False) +        self.object = self.get_object_or_none() + +        if self.object is None:              created = True              save_kwargs = {'force_insert': True}              success_status_code = status.HTTP_201_CREATED @@ -137,14 +144,22 @@ class UpdateModelMixin(object):          return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) +    def partial_update(self, request, *args, **kwargs): +        kwargs['partial'] = True +        return self.update(request, *args, **kwargs) +      def pre_save(self, obj):          """          Set any attributes on the object that are implicit in the request.          """          # pk and/or slug attributes are implicit in the URL. +        lookup = self.kwargs.get(self.lookup_field, None)          pk = self.kwargs.get(self.pk_url_kwarg, None)          slug = self.kwargs.get(self.slug_url_kwarg, None) -        slug_field = slug and self.get_slug_field() or None +        slug_field = slug and self.slug_field or None + +        if lookup: +            setattr(obj, self.lookup_field, lookup)          if pk:              setattr(obj, 'pk', pk) @@ -155,14 +170,13 @@ class UpdateModelMixin(object):          # Ensure we clean the attributes so that we don't eg return integer          # pk using a string representation, as provided by the url conf kwarg.          if hasattr(obj, 'full_clean'): -            exclude = _get_validation_exclusions(obj, pk, slug_field) +            exclude = _get_validation_exclusions(obj, pk, slug_field, self.lookup_field)              obj.full_clean(exclude)  class DestroyModelMixin(object):      """      Destroy a model instance. -    Should be mixed in with `SingleObjectAPIView`.      """      def destroy(self, request, *args, **kwargs):          obj = self.get_object()  | 
