How to Not allow the PUT method at all but allow PATCH in a DRF ViewSet?
Instead of using mixins.UpdateModelMixin
just define your own mixin that would perform patch only:
class UpdateModelMixin(object):
"""
Update a model instance.
"""
def partial_update(self, request, *args, **kwargs):
partial = True
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
If you want to use builtin mixins.UpdateModelMixin
, limit to PATCH
and disable swagger from showing PUT you can use http_method_names
class UserViewSet(mixins.UpdateModelMixin, GenericViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
http_method_names = ["patch"]
A simple and straight forward approach:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
http_method_names = ['get', 'post', 'patch'] # <---------
Like this the PUT
method will not be allowed.
I think a superior solution would be to use a custom router and disable the route for PUT. Then use your custom router for the viewsets.
class SimpleRouter(BaseRouter):
routes = [
# List route.
Route(
url=r'^{prefix}{trailing_slash}$',
mapping={
'get': 'list',
'post': 'create'
},
name='{basename}-list',
initkwargs={'suffix': 'List'}
),
# Dynamically generated list routes.
# Generated using @list_route decorator
# on methods of the viewset.
DynamicListRoute(
url=r'^{prefix}/{methodname}{trailing_slash}$',
name='{basename}-{methodnamehyphen}',
initkwargs={}
),
# Detail route.
Route(
url=r'^{prefix}/{lookup}{trailing_slash}$',
mapping={
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
},
name='{basename}-detail',
initkwargs={'suffix': 'Instance'}
),
# Dynamically generated detail routes.
# Generated using @detail_route decorator on methods of the viewset.
DynamicDetailRoute(
url=r'^{prefix}/{lookup}/{methodname}{trailing_slash}$',
name='{basename}-{methodnamehyphen}',
initkwargs={}
),
]
^ The router implementation looks something like that. So you just need to inherit the SimpleRouter
, or perhaps the DefaultRouter
, and defines the routes
class attribute how you want it. You can remove the mapping for 'put' in the Route(mapping={...})
completely, or you can define your own action to handle it and return the appropriate 400-something resonse.