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 | |
Natim
commented
Mar 28, 2018
•
from rest_framework import status
If you want to support overridable hooks perform_create and/or perform_update similar to what are available in CreateModelMixin and UpdateModelMixin ( documented in http://www.django-rest-framework.org/api-guide/generic-views/ ) you could modify the update function above as follows:
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}
self.perform_create(serializer, **extra_kwargs)
return Response(serializer.data, status=status.HTTP_201_CREATED)
self.perform_udpate(serializer)
return Response(serializer.data)
def perform_create(self, serializer, **kwargs):
serializer.save(**kwargs)
def perform_udpate(self, serializer):
serializer.save()
The mixin works fine, but how can i make optional the pk field of my object? is it possible?
I have problem with this fixture - I am getting duplicate errors if the request is called more than once quickly. Does anyone had the same problem? I tried @transaction.atomic
, but without success.
with support for perform_create and perform_update hooks (without changing signature)
and multiple-field mixins.
class PutAsCreateMixin(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:
self.perform_create(serializer)
return Response(serializer.data, status=status.HTTP_201_CREATED)
self.perform_update(serializer)
return Response(serializer.data)
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
def perform_create(self, serializer):
if not hasattr(self, 'lookup_fields'):
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}
else:
# set kwargs for additional fields
extra_kwargs = {
field: self.kwargs[field]
for field in self.lookup_fields if self.kwargs[field]
}
serializer.save(**extra_kwargs)
def perform_update(self, serializer):
serializer.save()
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
this way, additional attributes can also be added during creation.
Are there any plans to have something like this included in DRF by default?
Nope.