Delete method on Django Rest Framework ModelViewSet
Asked Answered
B

2

24

i have tried to delete a single ManuscriptItem instance using Postman to perform my API requests on against the view below:

class ManuscriptViewSet(viewsets.ModelViewSet):
    """Handles creating, reading and updating items."""

    authentication_classes = (TokenAuthentication,)
    serializer_class = serializers.ManuscriptItemSerializer
    permission_classes = (permissions.PostOwnManuscript, IsAuthenticated,)

    def perform_create(self, serializer):
        """Sets the user profile to the logged in user."""
        serializer.save(author=self.request.user)

    def get_queryset(self):
        """
        This view should return a list of all the manuscripts
        for the currently authenticated user.
        """
        user = self.request.user
        return models.ManuscriptItem.objects.filter(author=user)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

The destroy and perform destroy functions are what I have attempted without success. This is what it returns when i tried:

{ "detail": "Method \"DELETE\" not allowed." }

This is how my URLs are currently registered:

router = DefaultRouter()
router.register('manuscripts', views.ManuscriptViewSet, base_name="manuscripts")  # auto basename for models
router.register('manuscriptlibrary', views.ManuscriptLibraryViewSet, base_name="manuscript_library")
router.register('manuscriptsettings', views.ManuscriptSettingsViewSet)


urlpatterns = [
    url(r'', include(router.urls))
]

I'm i modifying the ModelViewSet wrong do i need to use another approach because of the nature of ModelViewSet? i expected it to work on Postman when i used an Authorized user to Delete a ManuscriptItem instance. In the docs it said Destroy() method can be used.

Additional information

The URL used is:

http://localhost:8000/manuscripts-api/manuscripts/

The model instance to be deleted from:

class ManuscriptItem(models.Model):
    """Represents a single manuscript's content"""

    author = models.ForeignKey('accounts_api.UserProfile', on_delete=models.CASCADE)
    title = models.CharField(max_length=255)
    content = models.CharField(max_length=99999999)

    def __str__(self):
        """Django uses when it needs to convert the object to a string"""
        return str(self.id)

The way i have tried sending delete requests on postman with json:

{
    "manuscript": 7,
}

Results: Delete Method not allowed

{
    "id": 7,
    "author": 5,
    "title": "niceone",
    "content": "niceone"
}

Results: Delete Method not allowed

Additional Questions/Info:

Don't i need to specify the router register with a pk? I tried this but didnt work either:

router.register('manuscripts/{pk}/$', views.ManuscriptViewSet, base_name="manuscript_detail")

Postman says:

Allow →GET, POST, HEAD, OPTIONS

Bigamist answered 5/9, 2017 at 11:30 Comment(1)
I have a question, new to DRF. Why to define custom methods in ModelViewSet. I am using below ModelViewSet, and don't need to define any methods. I can GET,POST, PUT & DELETE class ServerViewSet(viewsets.ModelViewSet): queryset = Server.objects.all() serializer_class = ServerSerializer url = f'http://127.0.0.1:8000/servers/5' response = requests.request( "DELETE", url, headers=headers, ) Cerellia
B
38

The issue here is that you send DELETE request to the wrong url. Look at the DefaultRouter docs. It generates automatically your urls within your viewset:

DefaultRouter class url patterns

Look closely at the DELETE method. It is on the {prefix}/{lookup}/[.format] url pattern. This means that your corresponding router url is manuscripts/<manuscript_id>/, but you try to send DELETE request to manuscripts/ only, which is the above pattern. You see directly from the table that the allowed HTTP methods there are GET and POST only. That's why you receive MethodNotAllowed.

The solution to your problem is not to pass the manuscript_id as a JSON body of the request

{
    "manuscript": 7,
}

But to pass it directly to the url:

DELETE http://localhost:8000/manuscripts-api/manuscripts/7/

And you just register your viewset like:

router.register(r'manuscripts', ManuscriptViewSet.as_view(), name='manuscripts')

As you see, DRF generates the urls automatically for you.

Brunel answered 5/9, 2017 at 12:5 Comment(2)
I try it but i get this error when register url with as_view() TypeError: The `actions` argument must be provided when calling `.as_view()` on a ViewSet. For example `.as_view({'get': 'list'})` do you know why?Donne
@AngelF I think that when you use ViewSet, not ModelViewSet for example, you should provide method -> action mapping (which is exactly this {'get': 'list'}. You tell that get method will invoke the list method. On ModelViewSet this is already set.Brunel
W
11
from rest_framework.response import Response
from rest_framework import status



def destroy(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            self.perform_destroy(instance)
        except Http404:
            pass
        return Response(status=status.HTTP_204_NO_CONTENT)

use this and it will work

Wire answered 5/9, 2017 at 11:41 Comment(3)
Hi i went with perform_destroy. It still tells me "Method \"DELETE\" not allowed." not allowed, i therefore added additional information.Bigamist
It still says method not allowed, i'm a sending in the correct json format? how do i tell it precisely which manuscriptitem to delete? i updated my question with additional information including the queryBigamist
I can't tell if "Response" is a Django thing or Django rest framework thing. The examples from Django rest framework have the same thing but don't show where to import it from and typically you'd have Django return an HttpResponse. "Response" is not in the Django docs. Can you please clarify this? UPDATE: I found that you import that with from rest_framework.response import ResponseCyclo

© 2022 - 2024 — McMap. All rights reserved.