Reputation: 26538
DRF version is 3.11.2
I was working on some existing code to add an extra "delete" action to a viewset, but I got 405 Method not allowed
, consider the following made up example
class UserViewSet(ModelViewSet):
....
queryset = User.objects.all()
http_method_names = ['get', 'post']
@action(detail=True, method=['delete'])
def delete_profile(self, request, pk=None)
Profile.objects.get(user=pk).delete()
....
Like I said, when I make a delete method call to /user/123/delete_profile
, I get 405 Method not allowed
, however, if I add delete
to http_method_names
as below, it works fine.
class UserViewSet(ViewSet):
....
queryset = User.objects.all()
http_method_names = ['get', 'post', 'delete']
@action(detail=True, method=['delete'])
def delete_profile(self, request, pk=None)
Profile.objects.get(user=pk).delete()
....
But this is not ideal as I don't want to allow "delete" operation on the User, whilst I can overwrite the delete
/destroy
method on the viewset to avoid user being deleted, this is not very elegant.
So the question is, is this a bug or by design? By design I mean the extra actions decorated by @action
suppose to be manipulating the same model defined in the ModelViewSet
(in this case User and not Profile)?
Upvotes: 2
Views: 3024
Reputation: 1828
The issue is with setting the http_method_names
attribute to the ModelViewSet. since you have set it to only get
and post
, other methods ( put
, patch
, delete
) will not work on the class. either you have to add the delete
method to http_method_names
attribute or remove the whole attribute to fix this as shown below.
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
@action(detail=True, methods=['delete'])
def delete_profile(self, request, pk=None):
Profile.objects.get(user=pk).delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Also if you dont want other methods than get
,post
,delete
to work, why not use mixins ?
Update : if you dont want the delete method to work on the main viewset, just override it as shown.
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
@action(detail=True, methods=['delete'])
def delete_profile(self, request, pk=None):
....
def destroy(self, request, *args, **kwargs):
return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
Upvotes: 2