Gonzalo Dambra
Gonzalo Dambra

Reputation: 980

Understanding Django Rest Framework's ModelViewSet Router

I have my Comment model:

class Comment(models.Model):
    """A comment is a content shared by a user in some post.""" 

    user = models.ForeignKey('users.User', on_delete=models.CASCADE, null=False)
    post = models.ForeignKey('posts.Post', on_delete=models.CASCADE, null=False)

    content = models.TextField(max_length=1000, null=False, blank=False)

    def __str__(self):
        """Return the comment str."""
        return "'{}'".format(self.content)

Its serializer:

class CommentSerializer(serializers.ModelSerializer):
    """Comment model serializer."""

    user = serializers.PrimaryKeyRelatedField(read_only=True)

    class Meta:
        model = Comment
        fields = '__all__'

    def create(self, validated_data):
        """Create a new comment in some post, by request.user."""

        validated_data['user'] = self.context['request'].user
        return super().create(validated_data)

    def list(self, request):
        """List all the comments from some post."""

        if 'post' not in request.query_params:
            raise ValidationError('Post id must be provided.')

        q = self.queryset.filter(post=request.query_params['post'])
        serializer = CommentSerializer(q, many=True)
        return Response(serializer.data)

The viewset:

class CommentViewSet(viewsets.ModelViewSet):

    serializer_class = CommentSerializer
    queryset = Comment.objects.all()

    def get_permissions(self):
        permissions = []
        if self.action == 'create' or self.action == 'destroy':
            permissions.append(IsAuthenticated)
        return [p() for p in permissions]

    def get_object(self):
        """Return comment by primary key."""
        return get_object_or_404(Comment, id=self.kwargs['pk'])  # this is the drf's get_object_or_404 function

    def destroy(self, request, *args, **kwargs):
        """Delete a comment created by request.user from a post."""
        pdb.set_trace()
        instance = self.get_object()
        if instance.user != request.user:
            raise ValidationError('Comment does not belong to the authenticated user.')
        self.perform_destroy(instance)

    def retrieve(self, request, pk=None):
        pass

    def update(self, request, pk=None):
        pass

    def partial_update(self, request, pk=None):
        pass

So far so good when it comes to list, create and retrieve. But when it comes to delete/destroy (Idk the difference) I don't know how to get the URL for the DELETE request. I'm using Postman to do so.

Urls.py:

router = routers.SimpleRouter()
router.register(r'comments', CommentViewSet, basename='comments')

pdb.set_trace()  # Put a pdb here to see what does router.urls have.

urlpatterns = [
    # Non api/simple django views
    path('create_comment/', create_comment, name='create_comment'),
    path('delete_comment/', delete_comment, name='delete_comment'),
    # rest api views
    path('rest/', include(router.urls))
]

When I debug the router.urls, the terminal shows this:

[<URLPattern '^comments/$' [name='comments-list']>, <URLPattern '^comments/(?P<pk>[^/.]+)/$' [name='comments-detail']>]

Why there is no url for create and destroy? I could create some Comments from the api, and I didn't even know how did I get the POST request url for the create function, I just guessed lol, but that's not what you want when you're programming right? I have checked the Routers documentation but I don't get it. Please give some help! Many thanks.

Upvotes: 0

Views: 403

Answers (1)

JPG
JPG

Reputation: 88499

The answer is simple, Django uses the comments-list list for create and list operation and comments-detail for update and delete operations.

That is, there are only two URL end-points, but it supports several actions, which can be performed by changing the HTTP methods

You can use
HTTP GET foo-bar/comments/ to retrieve all comments where as
HTTP POST foo-bar/comments/ can be used to create a new comment.

Upvotes: 1

Related Questions