My goal is to implement adding a recipe to current user favorites list and removing it using Django Rest Framework 3.12.x.
POST api/recipes/{int:recipe_id}/favorite
DELETE api/recipes/{int:recipe_id}/favorite
Request body is empty. User_id is got from self.context[‘request’].user.id.
I.e. I need to serve POST and DELETE on the same URL.
I implemented that against DefaultRouter in conjunction with GenericViewSet, but can’t get this implementation serving DELETE. Only POST is served properly.
The quesrtion is: what’s wrong in the configuration? Why DRF returns 405 “Method Not Allowed” on DELETE calls and returns Http header Allow: POST, OPTIONS?
Router configuration:
from rest_framework import routers
from api.views import FavoriteViewSet
_v1_router = routers.DefaultRouter()
_v1_router.register(
r'recipes/(?P<recipe_id>d+)/favorite',
FavoriteViewSet,
basename='favorite')
View:
class FavoriteViewSet(
mixins.CreateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
permission_classes = (IsAuthenticated,)
http_method_names = ('post', 'delete', 'head', 'options',)
queryset = Favorite.objects.all()
serializer_class = FavoriteSerializer
pagination_class = None
def get_permissions(self):
return super().get_permissions()
def get_object(self):
return super().get_object()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
def destroy(self, request, *args, **kwargs):
super().destroy(request, *args, **kwargs)
def get_serializer_class(self):
return super().get_serializer_class()
def dispatch(self, request, *args, **kwargs):
p1 = hasattr(self, 'get')
p2 = hasattr(self, 'post')
p3 = hasattr(self, 'delete')
p4 = hasattr(self, 'options')
return super().dispatch(request, *args, **kwargs)
Having spent some time in the Debugger, I figured out, that router configures two instances of my View, but serves reqyuest each time on the first one.
def as_view(cls, actions=None, initkwargs) – got called twice during initialization.
With parameters:
post: create
suffix: List, basename: favorite, detail: False
get: retrieve, delete: destroy
suffix: Instance, basename: favorite, detail: True
I see,that something is wrong with configuration, but couldn’t figure out yet how make DELETE request to the same URL being served on the second instance.
Under the Debugger I see thet in dispatch:
def dispatch(self, request, *args, **kwargs):
p1 = hasattr(self, 'get')
p2 = hasattr(self, 'post')
p3 = hasattr(self, 'delete')
p4 = hasattr(self, 'options')
return super().dispatch(request, *args, **kwargs)
On picked view instance only p2 and p4 are True, so, view does not see ‘delete’ attribute presenting in my view and rejects the request with 405.