2

I'm using Django Rest Framework and want to be able to delete a Content instance via DELETE to /api/content/<int:pk>/. I don't want to implement any method to respond to GET requests.

When I include a .retrieve() method as follows, the DELETE request works:

class ContentViewSet(GenericViewSet):
    def get_queryset(self):
        return Content.objects.filter(user=self.request.user)

    def retrieve(self, request, pk=None):
        pass    #this works, but I don't want .retrieve() at all

    def delete(self, request, pk=None):
        content = self.get_object()
        #look up some info info here
        content.delete()
        return Response('return some info')

If I replace .retrieve() with RetrieveModelMixin it also works. However, if I remove both of these, which is what want to do, I get the following error.

django.urls.exceptions.NoReverseMatch: Reverse for 'content-detail' not found. 'content-detail' is not a valid view function or pattern name.

I haven't tested, but I assume the same thing would happen with PUT and PATCH.

My questions are:

  1. How can I allow DELETE without implementing a .retrieve() method, and
  2. Why can't DRF create the urlconf without .retrieve() implemented?

UPDATE: Failing test and complete error traceback caused by removing .retrieve() method

from rest_framework.test import APITestCase, APIClient
from myapp.models import Content

class ContentTestCase(APITestCase):
    def setUp(self):
        self.content = Content.objects.create(title='New content')
        self.client = APIClient()

    def test_DELETE_content(self):
        url = reverse('content-detail', kwargs={'pk':self.content.pk})
        response = self.client.delete(url)
        self.assertEqual(response.status_code, 200)

Results in:

Traceback (most recent call last):
  File "myproject/myapp/tests.py", line 548, in test_DELETE_content
    url = reverse('content-detail', kwargs={'pk':self.content})
  File "python3.6/site-packages/rest_framework/reverse.py", line 50, in reverse
    url = _reverse(viewname, args, kwargs, request, format, **extra)
  File "python3.6/site-packages/rest_framework/reverse.py", line 63, in _reverse
    url = django_reverse(viewname, args=args, kwargs=kwargs, **extra)
  File "python3.6/site-packages/django/urls/base.py", line 90, in reverse
    return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))
  File "python3.6/site-packages/django/urls/resolvers.py", line 636, in _reverse_with_prefix
    raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'content-detail' not found. 'content-detail' is not a valid view function or pattern name.
1

3 Answers 3

2
  1. How can I allow DELETE without implementing a .retrieve() method?

Just remove the retrieve() method from the view class. Which means, the GenericViewSet doesn't provide any HTTP Actions unless it's defined in your class.

So, the following will be your code snippet,

class ContentViewSet(GenericViewSet):

    def get_queryset(self):
        return Content.objects.filter(user=self.request.user)

    def delete(self, request, pk=None):
        content = self.get_object()
        # look up some info info here
        content.delete()
        return Response('return some info')

or you could use mixin classes here,

from rest_framework.mixins import DestroyModelMixin


class ContentViewSet(DestroyModelMixin, GenericViewSet):

    def get_queryset(self):
        return Content.objects.filter(user=self.request.user)

  1. Why can't DRF create the urlconf without .retrieve() implemented?

I'm not sure how you've defined your URLs. When I tried with DRF Router, it's only creating the URL conf for defined actions.

You've got GET and DELETE actions on your end-point because of you'd defined the retrieve() method in your view class.

Hope this help :)

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks but in my question I mentioned that I had already tried removing the retrieve() method... and the result was that delete() no longer worked because drf could not find the reverse url.
Are you sure 😳 because, the reverse urls has no effect on DRF's delete method
And, i don't see any code snippet related to that problem in your given code as well as in the source code.
Yes I'm sure that a DELETE request to reverse('content-detail') only works for me if retrieve() method is present, else I get NoReverseMatch as shown in the question. I simply tested by including and then removing retrieve(). Was also able to use RetrieveModelMixin to same effect. Since my code passes the test, this is mainly a curiosity for now
can you add complete error traceback to the OP?
|
1

Wild guess here but did you use a SimpleRouter or a DefaultRouter to build your urlpatterns?

If so, that's your problem. The router uses a viewset and expects to have all methods implemented. More info here

What you can do is just add your url to urlpatterns like you would normally do on django using the .as_view() method.

2 Comments

I did use a DefaultRouter. I will check and see the result
Your solution is the most direct, but the reason why I chose GenericViewSet was to incorporate auto-routing like normal DRF viewsets, without implementing any actions by default (so that I could pick and choose).
0

My solution for part 1. is to include the mixin but restrict the http_method_names:

class ContentViewSet(RetrieveModelMixin, GenericViewSet):
    http_method_names = ['delete']
    ...

However, I still don't know why I have to include RetrieveModelMixin at all.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.