Delete method on Django Rest Framework ModelViewSe

2020-06-03 06:53发布

问题:

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

回答1:

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:

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.



回答2:

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