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.