diff options
| author | Eleni Lixourioti | 2014-11-15 14:27:41 +0000 |
|---|---|---|
| committer | Eleni Lixourioti | 2014-11-15 14:27:41 +0000 |
| commit | 1aa77830955dcdf829f65a9001b6b8900dfc8755 (patch) | |
| tree | 1f6d0bea3c0fe720a298b2da177bb91e8a74a19c /rest_framework/mixins.py | |
| parent | afaa52a378705b7f0475d5ece04a2cf49af4b7c2 (diff) | |
| parent | 88008c0a687219e3104d548196915b1068536d74 (diff) | |
| download | django-rest-framework-1aa77830955dcdf829f65a9001b6b8900dfc8755.tar.bz2 | |
Merge branch 'version-3.1' of github.com:tomchristie/django-rest-framework into oauth_as_package
Conflicts:
.travis.yml
Diffstat (limited to 'rest_framework/mixins.py')
| -rw-r--r-- | rest_framework/mixins.py | 170 |
1 files changed, 51 insertions, 119 deletions
diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 2cc87eef..14a6b44b 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -6,40 +6,11 @@ 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 +18,11 @@ class CreateModelMixin(object): Create a model instance. """ 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(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) + serializer = self.get_serializer(data=request.DATA) + serializer.is_valid(raise_exception=True) + serializer.save() + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def get_success_headers(self, data): try: @@ -70,31 +35,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 +50,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,29 +61,52 @@ class UpdateModelMixin(object): """ def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) - self.object = self.get_object_or_none() + instance = self.get_object() + serializer = self.get_serializer(instance, data=request.DATA, partial=partial) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data) - serializer = self.get_serializer(self.object, data=request.DATA, - files=request.FILES, partial=partial) + def partial_update(self, request, *args, **kwargs): + kwargs['partial'] = True + return self.update(request, *args, **kwargs) - 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) +class DestroyModelMixin(object): + """ + Destroy a model instance. + """ + def destroy(self, request, *args, **kwargs): + instance = self.get_object() + instance.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + + +# The AllowPUTAsCreateMixin was previously the default behaviour +# for PUT requests. This has now been removed and must be *explictly* +# included if it is the behavior that you want. +# For more info see: ... + +class AllowPUTAsCreateMixin(object): + """ + The following mixin class may be used in order to support PUT-as-create + behavior for incoming requests. + """ + def update(self, request, *args, **kwargs): + partial = kwargs.pop('partial', False) + instance = self.get_object_or_none() + serializer = self.get_serializer(instance, data=request.DATA, partial=partial) + serializer.is_valid(raise_exception=True) + + if instance is None: + lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field + lookup_value = self.kwargs[lookup_url_kwarg] + extras = {self.lookup_field: lookup_value} + serializer.save(extras=extras) return Response(serializer.data, status=status.HTTP_201_CREATED) - self.object = serializer.save(force_update=True) - self.post_save(self.object, created=False) - return Response(serializer.data, status=status.HTTP_200_OK) + serializer.save() + return Response(serializer.data) def partial_update(self, request, *args, **kwargs): kwargs['partial'] = True @@ -156,41 +126,3 @@ class UpdateModelMixin(object): # 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) - return Response(status=status.HTTP_204_NO_CONTENT) |
