Search code examples
django-rest-frameworkdjango-rest-viewsets

How to send PUT request to ModelViewSet without passing a primary key in the url?


I am particularly interested in using ModelViewSet for solving the challenge of updating the logged in user's profile. I am using the following definition:

from rest_framework import viewsets

class ProfileRetrieveUpdate(viewsets.ModelViewSet):
    serializer_class = UserProfileSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

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

    def perform_update(self, serializer):
        serializer.save(user=self.request.user)

By overriding get_queryset, I am able to get the expected behavior, i.e. when I access the endpoints (say profile/), I get the profile of the logged in user. However, if I have to update the profile, I can only access PUT by going to profile/6/. Is there a way I can get ModelViewSet to expose PUT at the same endpoint i.e. profile/?


Solution

  • You can register ModelViewSet HTTP method under any path you want.

    path(
        "profile",
        ProfileRetrieveUpdate.as_view(
            {"put": "partial_update", "get": "retrieve"}
        ),
        name="profile-retrieve-update",
    )
    

    You will have to adjust other things as well as you don't provide pk. Instead of overriding get_queryset method you need to adjust get_object for your needs.

    from rest_framework import viewsets
    from rest_framework.generics import get_object_or_404
    
    
    class ProfileRetrieveUpdate(viewsets.ModelViewSet):
        serializer_class = UserProfileSerializer
        permission_classes = [permissions.IsAuthenticatedOrReadOnly]
        queryset = UserProfile.objects.all()
    
        def get_object(self):
            obj = get_object_or_404(self.queryset, user=self.request.user)
            self.check_object_permissions(self.request, obj)
            return obj
    
        def perform_update(self, serializer):
            serializer.save(user=self.request.user)
    
    

    Now if you PUT profile/ it should work as expected.