Home > Back-end >  Django Rest Framework User owned object: modifying a related object
Django Rest Framework User owned object: modifying a related object

Time:09-16

Suppose I have models like:

class Book(models.Model):
    title = models.CharField(max_length=254)
    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)

class Chapter(models.Model):
    title = models.CharField(max_length=254)
    book = models.ForeignKey(Book, on_delete=models.CASCADE)

Suppose user A creates book. I can restrict access to the book from user B in the Book viewset by overriding get_queryset:

 def get_queryset(self):
     user = User.objects.get(username=self.request.user.username)
     queryset = Book.objects.filter(user=user)
     return queryset

I also have an endpoint for adding chapters:

class ChapterViewSet(viewsets.ModelViewSet):
    queryset = Chapter.objects.all()
    serializer_class = serializers.ChapterSerializer
    model = Chapter

I want to prevent user B from adding (creating, POST) a chapter to a book created by user A. What is the best practice for creating this restriction?

CodePudding user response:

I would definetly do it on serializer level with validation ...

https://www.django-rest-framework.org/api-guide/serializers/#validation

so if your serializer looks smth like this i would do:

class ChapterSerializer(serializers.ModelSerializer):
    class Meta:
        model = Chapter
        fields = ['id', 'title', 'book']

    def validate_book(self, value):
       if not Book.objects.filter(id=value, user=self.context['request'].user).exists():
           raise serializers.ValidationError("This is not your book.")

CodePudding user response:

That can be achieved by overwriting the perform_create method. You should also modify the get_queryset method to achieve what you are looking for. You could do something like:

# your_app/views.py

from rest_framework import exceptions, viewsets

from .models import Book, Chapter


class ChapterViewSet(viewsets.ModelViewSet):
    queryset = Chapter.objects.all()
    serializer_class = serializers.ChapterSerializer
    model = Chapter

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

    def perform_create(self, serializer):
        book = serializer.validated_data['book']
        if book.user != self.request.user:
            raise exceptions.PermissionDenied("Only book owners can add chapter to it.")
        serializer.save()

Now, if an API call that attempts to add a chapter to a book that is not created by the authenticated user is made, a 403 Permission Denied error is returned in the response.

  • Related