aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework/mixins.py
diff options
context:
space:
mode:
authorEleni Lixourioti2014-11-15 14:27:41 +0000
committerEleni Lixourioti2014-11-15 14:27:41 +0000
commit1aa77830955dcdf829f65a9001b6b8900dfc8755 (patch)
tree1f6d0bea3c0fe720a298b2da177bb91e8a74a19c /rest_framework/mixins.py
parentafaa52a378705b7f0475d5ece04a2cf49af4b7c2 (diff)
parent88008c0a687219e3104d548196915b1068536d74 (diff)
downloaddjango-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.py170
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)