diff options
Diffstat (limited to 'rest_framework/mixins.py')
| -rw-r--r-- | rest_framework/mixins.py | 155 | 
1 files changed, 24 insertions, 131 deletions
| diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 2cc87eef..2074a107 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -6,40 +6,9 @@ which allows mixin classes to be composed in interesting ways.  """  from __future__ import unicode_literals -from django.core.exceptions import ValidationError -from django.http import Http404  from rest_framework import status  from rest_framework.response import Response -from rest_framework.request import clone_request  from rest_framework.settings import api_settings -import warnings - - -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. - -    For use when performing full_clean on a model instance, -    so we only clean the required fields. -    """ -    include = [] - -    if pk: -        # Deprecated -        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: -        # Deprecated -        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): @@ -47,17 +16,14 @@ class CreateModelMixin(object):      Create a model instance.      """      def create(self, request, *args, **kwargs): -        serializer = self.get_serializer(data=request.DATA, files=request.FILES) +        serializer = self.get_serializer(data=request.data) +        serializer.is_valid(raise_exception=True) +        self.perform_create(serializer) +        headers = self.get_success_headers(serializer.data) +        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) -        if serializer.is_valid(): -            self.pre_save(serializer.object) -            self.object = serializer.save(force_insert=True) -            self.post_save(self.object, created=True) -            headers = self.get_success_headers(serializer.data) -            return Response(serializer.data, status=status.HTTP_201_CREATED, -                            headers=headers) - -        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) +    def perform_create(self, serializer): +        serializer.save()      def get_success_headers(self, data):          try: @@ -70,31 +36,13 @@ class ListModelMixin(object):      """      List a queryset.      """ -    empty_error = "Empty list and '%(class_name)s.allow_empty' is False." -      def list(self, request, *args, **kwargs): -        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. -        if not self.allow_empty and not self.object_list: -            warnings.warn( -                'The `allow_empty` parameter is deprecated. ' -                'To use `allow_empty=False` style behavior, You should override ' -                '`get_queryset()` and explicitly raise a 404 on empty querysets.', -                DeprecationWarning -            ) -            class_name = self.__class__.__name__ -            error_msg = self.empty_error % {'class_name': class_name} -            raise Http404(error_msg) - -        # Switch between paginated or standard style responses -        page = self.paginate_queryset(self.object_list) +        instance = self.filter_queryset(self.get_queryset()) +        page = self.paginate_queryset(instance)          if page is not None:              serializer = self.get_pagination_serializer(page)          else: -            serializer = self.get_serializer(self.object_list, many=True) - +            serializer = self.get_serializer(instance, many=True)          return Response(serializer.data) @@ -103,8 +51,8 @@ class RetrieveModelMixin(object):      Retrieve a model instance.      """      def retrieve(self, request, *args, **kwargs): -        self.object = self.get_object() -        serializer = self.get_serializer(self.object) +        instance = self.get_object() +        serializer = self.get_serializer(instance)          return Response(serializer.data) @@ -114,83 +62,28 @@ class UpdateModelMixin(object):      """      def update(self, request, *args, **kwargs):          partial = kwargs.pop('partial', False) -        self.object = self.get_object_or_none() - -        serializer = self.get_serializer(self.object, data=request.DATA, -                                         files=request.FILES, partial=partial) - -        if not serializer.is_valid(): -            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - -        try: -            self.pre_save(serializer.object) -        except ValidationError as err: -            # full_clean on model instance may be called in pre_save, -            # so we have to handle eventual errors. -            return Response(err.message_dict, status=status.HTTP_400_BAD_REQUEST) - -        if self.object is None: -            self.object = serializer.save(force_insert=True) -            self.post_save(self.object, created=True) -            return Response(serializer.data, status=status.HTTP_201_CREATED) +        instance = self.get_object() +        serializer = self.get_serializer(instance, data=request.data, partial=partial) +        serializer.is_valid(raise_exception=True) +        self.perform_update(serializer) +        return Response(serializer.data) -        self.object = serializer.save(force_update=True) -        self.post_save(self.object, created=False) -        return Response(serializer.data, status=status.HTTP_200_OK) +    def perform_update(self, serializer): +        serializer.save()      def partial_update(self, request, *args, **kwargs):          kwargs['partial'] = True          return self.update(request, *args, **kwargs) -    def get_object_or_none(self): -        try: -            return self.get_object() -        except Http404: -            if self.request.method == 'PUT': -                # For PUT-as-create operation, we need to ensure that we have -                # relevant permissions, as if this was a POST request.  This -                # will either raise a PermissionDenied exception, or simply -                # return None. -                self.check_permissions(clone_request(self.request, 'POST')) -            else: -                # PATCH requests where the object does not exist should still -                # return a 404 response. -                raise - -    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_url_kwarg = self.lookup_url_kwarg or self.lookup_field -        lookup = self.kwargs.get(lookup_url_kwarg, None) -        pk = self.kwargs.get(self.pk_url_kwarg, None) -        slug = self.kwargs.get(self.slug_url_kwarg, None) -        slug_field = slug and self.slug_field or None - -        if lookup: -            setattr(obj, self.lookup_field, lookup) - -        if pk: -            setattr(obj, 'pk', pk) - -        if slug: -            setattr(obj, slug_field, slug) - -        # 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, self.lookup_field) -            obj.full_clean(exclude) -  class DestroyModelMixin(object):      """      Destroy a model instance.      """      def destroy(self, request, *args, **kwargs): -        obj = self.get_object() -        self.pre_delete(obj) -        obj.delete() -        self.post_delete(obj) +        instance = self.get_object() +        self.perform_destroy(instance)          return Response(status=status.HTTP_204_NO_CONTENT) + +    def perform_destroy(self, instance): +        instance.delete() | 
