Suppose we have two models:
class Chapter(models.Model):
title = models.CharField(max_length=128)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
class Post(models.Model):
title = models.CharField(max_length=128)
body = models.TextField()
is_archived = models.BooleanField(default=False)
chapter = models.ForeignKey(Chapter, on_delete=models.CASCADE)
And default ModelViewSet
viewset for Chapter
model:
class ChapterViewSet(viewsets.ModelViewSet):
queryset = Chapter.objects.all()
serializer_class = ChapterSerializer
The key thing is that ChapterSerializer
performs nested serialization using PostSerializer
to provide a post_set
key in the response.
class PostSerializer(serializers.HyperlinkedModelSerializer):
detail_url = HyperlinkedIdentityField(view_name='post-detail', read_only=True)
class Meta:
fields = ['id', 'title', 'is_archived', 'detail_url']
model = Post
class ChapterSerializer(serializers.ModelSerializer):
post_set = PostSerializer(read_only=True, many=True)
class Meta:
model = Chapter
fields = ['id', 'title', 'owner', 'post_set']
The question is how I can dynamically specify a queryset for this nested PostSerializer
. For example, when user makes GET
request I only want to include the posts that are not archived (is_archived
field is set to False
) if user, who has done a request, is not an owner of a Chapter
(request.user != current_chapter.owner
). Is there any way to achive it?
CodePudding user response:
You can use prefetch_related to prefetch the results used by a nested serializer, this prefetch can be filtered by using a Prefetch object and this will then filter the nested results
class ChapterViewSet(viewsets.ModelViewSet):
queryset = Chapter.objects.all()
serializer_class = ChapterSerializer
def get_queryset(self):
queryset = super().get_queryset()
return queryset.prefetch_related(
Prefetch('post_set', queryset=Post.objects.filter(is_archived=False))
)
In the get_queryset
method you will have to perform this prefetching dynamically, the current request can be accessed via self.request