From d27b8cc09b83bac10346effa1021493d2835b794 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 3 Nov 2014 12:00:19 +0000 Subject: [PATCH] PUT as create docs, and move mixin out to external gist --- docs/api-guide/generic-views.md | 14 ++++++++++ docs/topics/3.0-announcement.md | 2 +- rest_framework/mixins.py | 48 --------------------------------- 3 files changed, 15 insertions(+), 49 deletions(-) diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 49be0cae8..d2836fa6d 100755 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -368,6 +368,20 @@ If you are using a mixin across multiple views, you can take this a step further Using custom base classes is a good option if you have custom behavior that consistently needs to be repeated across a large number of views throughout your project. +--- + +# PUT as create + +Prior to version 3.0 the REST framework mixins treated `PUT` as either an update or a create operation, depending on if the object already existed or not. + +Allowing `PUT` as create operations is problematic, as it necessarily exposes information about the existence or non-existance of objects. It's also not obvious that transparently allowing re-creating of previously deleted instances is necessarily a better default behavior than simply returning `404` responses. + +Both styles "`PUT` as 404" and "`PUT` as create" can be valid in different circumstances, but from version 3.0 onwards we now use 404 behavior as the default, due to it being simpler and more obvious. + +If you need to generic PUT-as-create behavior you may want to include something like [this `AllowPUTAsCreateMixin` class](https://gist.github.com/tomchristie/a2ace4577eff2c603b1b) as a mixin to your views. + +--- + # Third party packages The following third party packages provide additional generic view implementations. diff --git a/docs/topics/3.0-announcement.md b/docs/topics/3.0-announcement.md index f69c0b252..97ffdcf3f 100644 --- a/docs/topics/3.0-announcement.md +++ b/docs/topics/3.0-announcement.md @@ -694,7 +694,7 @@ Allowing `PUT` as create operations is problematic, as it necessarily exposes in Both styles "`PUT` as 404" and "`PUT` as create" can be valid in different circumstances, but we've now opted for the 404 behavior as the default, due to it being simpler and more obvious. -If you need to restore the previous behavior you can include the `AllowPUTAsCreateMixin` class in your view. This class can be imported from `rest_framework.mixins`. +If you need to restore the previous behavior you may want to include [this `AllowPUTAsCreateMixin` class](https://gist.github.com/tomchristie/a2ace4577eff2c603b1b) as a mixin to your views. #### Customizing error responses. diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 467ff5155..2074a1072 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -6,10 +6,8 @@ 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 from rest_framework.settings import api_settings @@ -89,49 +87,3 @@ class DestroyModelMixin(object): def perform_destroy(self, instance): instance.delete() - - -# The AllowPUTAsCreateMixin was previously the default behaviour -# for PUT requests. This has now been removed and must be *explicitly* -# 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] - extra_kwargs = {self.lookup_field: lookup_value} - serializer.save(**extra_kwargs) - return Response(serializer.data, status=status.HTTP_201_CREATED) - - serializer.save() - return Response(serializer.data) - - 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