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}/favoriteDELETE 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 routersfrom 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 = FavoriteSerializerpagination_class = Nonedef 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 request each time on the first one.
def as_view(cls, actions=None, initkwargs) - got called twice during initialization.With parameters:
post: createsuffix: List, basename: favorite, detail: Falseget: retrieve, delete: destroysuffix: Instance, basename: favorite, detail: True
I see,that something is wrong with configuration, but couldn't figure out yet how to make DELETE request to the same URL being served on the second instance.
Under the Debugger I see that 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 presense in my view and rejects the request with 405.