Skip to content

Instantly share code, notes, and snippets.

Created November 3, 2014 11:55
Show Gist options
  • Save tomchristie/a2ace4577eff2c603b1b to your computer and use it in GitHub Desktop.
Save tomchristie/a2ace4577eff2c603b1b to your computer and use it in GitHub Desktop.
PUT-as-create mixin class for Django REST framework.
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,, partial=partial)
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}**extra_kwargs)
return Response(, status=status.HTTP_201_CREATED)
return Response(
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
def get_object_or_none(self):
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'))
# PATCH requests where the object does not exist should still
# return a 404 response.
Copy link

Are there any plans to have something like this included in DRF by default?

Copy link


Copy link

This seems to work for me with Django 4.2 and Python 3.11. Although I haven't tested it with permissions.

from django.http
import rest_framework.mixins

class AllowPUTAsCreateMixin(rest_framework.mixins.CreateModelMixin, rest_framework.mixins.UpdateModelMixin):

    def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
        except Http404:
        return self.create(request, *args, **kwargs)

With this I can create a view that allows retrieving via GET and upsert via PUT.

import rest_framework.generics
import rest_framework.serializers

class Serializer(rest_framework.serializers.ModelSerializer):
    ...  # TODO: As normal

class RetrieveCreatePatch(AllowPUTAsCreateMixin, rest_framework.generics.RetrieveAPIView):
    queryset = MyModel.objects.all()
    serializer_class = Serializer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment