diff options
| author | Tom Christie | 2013-03-18 21:03:05 +0000 |
|---|---|---|
| committer | Tom Christie | 2013-03-18 21:03:05 +0000 |
| commit | 74fb366c595db87bb71baeffcacfb7d2482e3a18 (patch) | |
| tree | 2e28cb52542742f32cdd3fbeb625f7f59cba0a3f /rest_framework/mixins.py | |
| parent | 4c6396108704d38f534a16577de59178b1d0df3b (diff) | |
| parent | 034c4ce4081dd6d15ea47fb8318754321a3faf0c (diff) | |
| download | django-rest-framework-74fb366c595db87bb71baeffcacfb7d2482e3a18.tar.bz2 | |
Merge branch 'master' into resources-routers
Diffstat (limited to 'rest_framework/mixins.py')
| -rw-r--r-- | rest_framework/mixins.py | 67 |
1 files changed, 53 insertions, 14 deletions
diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index e0ae216e..7d9a6e65 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -4,22 +4,48 @@ Basic building blocks for generic class based views. We don't bind behaviour to http method handlers yet, which allows mixin classes to be composed in interesting ways. """ +from __future__ import unicode_literals + from django.http import Http404 from rest_framework import status from rest_framework.response import Response +from rest_framework.request import clone_request + + +def _get_validation_exclusions(obj, pk=None, slug_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: + 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: + include.append(slug_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 `BaseView`. + Should be mixed in with any `GenericAPIView`. """ def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.DATA, files=request.FILES) if serializer.is_valid(): self.pre_save(serializer.object) - self.object = serializer.save() + 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) @@ -38,7 +64,7 @@ class ListModelMixin(object): List a queryset. Should be mixed in with `MultipleObjectAPIView`. """ - empty_error = u"Empty list and '%(class_name)s.allow_empty' is False." + empty_error = "Empty list and '%(class_name)s.allow_empty' is False." def list(self, request, *args, **kwargs): queryset = self.get_queryset() @@ -60,7 +86,7 @@ class ListModelMixin(object): paginator, page, queryset, is_paginated = packed serializer = self.get_pagination_serializer(page) else: - serializer = self.get_serializer(self.object_list) + serializer = self.get_serializer(self.object_list, many=True) return Response(serializer.data) @@ -68,10 +94,12 @@ class ListModelMixin(object): class RetrieveModelMixin(object): """ Retrieve a model instance. - Should be mixed in with `SingleObjectBaseView`. + Should be mixed in with `SingleObjectAPIView`. """ def retrieve(self, request, *args, **kwargs): - self.object = self.get_object() + queryset = self.get_queryset() + filtered_queryset = self.filter_queryset(queryset) + self.object = self.get_object(filtered_queryset) serializer = self.get_serializer(self.object) return Response(serializer.data) @@ -79,23 +107,32 @@ class RetrieveModelMixin(object): class UpdateModelMixin(object): """ Update a model instance. - Should be mixed in with `SingleObjectBaseView`. + Should be mixed in with `SingleObjectAPIView`. """ def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) + self.object = None try: self.object = self.get_object() - success_status_code = status.HTTP_200_OK except Http404: - self.object = None + # 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')) + created = True + save_kwargs = {'force_insert': True} success_status_code = status.HTTP_201_CREATED + else: + created = False + save_kwargs = {'force_update': True} + success_status_code = status.HTTP_200_OK serializer = self.get_serializer(self.object, data=request.DATA, files=request.FILES, partial=partial) if serializer.is_valid(): self.pre_save(serializer.object) - self.object = serializer.save() + self.object = serializer.save(**save_kwargs) + self.post_save(self.object, created=created) return Response(serializer.data, status=success_status_code) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -106,24 +143,26 @@ class UpdateModelMixin(object): """ # pk and/or slug attributes are implicit in the URL. 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 + if pk: setattr(obj, 'pk', pk) - slug = self.kwargs.get(self.slug_url_kwarg, None) if slug: - slug_field = self.get_slug_field() 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'): - obj.full_clean() + exclude = _get_validation_exclusions(obj, pk, slug_field) + obj.full_clean(exclude) class DestroyModelMixin(object): """ Destroy a model instance. - Should be mixed in with `SingleObjectBaseView`. + Should be mixed in with `SingleObjectAPIView`. """ def destroy(self, request, *args, **kwargs): obj = self.get_object() |
