I have two models:
class Book(models.Model):
title = models.CharField(max_length=250)
author = models.CharField(max_length=250)
class WordInBook(models.Model):
book = models.ForeignKey("Book")
word = models.ForeignKey("Word")
And corresponding serializers:
class BookSerializer(ModelSerializer):
wordinbook_set = WordInBookSerializer(many=True)
class Meta:
model = Book
fields = ('id', 'title', 'author', 'wordinbook_set')
class WordInBookSerializer(ModelSerializer):
class Meta:
model = WordInBook
fields = ('word')
Now I want to paginate the wordinbook_set. Outside of serialiser it is easy:
book = Book.objects.get(pk=book_id)
paginator = Paginator(book.wordinbook_set.all(), 10)
words = paginator.page(page).object_list
But that leaves me with two separate serialized objects.
Question: how do I paginate wordinbook_set inside the serializer?
The resulting json should look like this:
{id: '...', title: '...', author: '...', wordinbook_set: [ 10 WordInBook objects here ]}
Since PaginationSerializer was removed in DRF 3.1, you have to implement your own logic, for further details refer to :
https://stackoverflow.com/a/31500287/7469841
So you have to change your BookSerializer to include the pagination behaviour as following :
BookSerializer
class BookSerializer(ModelSerializer):
wordinbook_set = serializers.SerializerMethodField('paginated_wordinbook')
class Meta:
model = Book
fields = ('id', 'title', 'author', 'wordinbook_set')
def paginated_wordinbook(self, obj):
page_size = self.context['request'].query_params.get('size') or 10
paginator = Paginator(obj.wordinbook_set.all(), page_size)
page = self.context['request'].query_params.get('page') or 1
words_in_book = paginator.page(page)
serializer = WordInBookSerializer(words_in_book, many=True)
return serializer.data
Firstly You have to use the Paginator found in django.core.paginator to paginate an iterable object:
paginator = Paginator(obj.wordinbook_set.all(), page_size)
Then get the target page from paginated data :
words_in_book = paginator.page(page)
Serialize the paginated set with many=True:
serializer = WordInBookSerializer(words_in_book, many=True)
Also to make the page size dynamic you can use query_params to receive the desired page size, for example you can choose the page size to be 10 in a request and be 100 in a different request, to retrieve the page size:
page_size = self.context['request'].query_params.get('size') or 10
And finally to allow the user to request a certain page, use again the query_params to receive it:
page = self.context['request'].query_params.get('page') or 1
Hmm, believe that you should tackle it little bit differently.
First - define the @detail_route
on the BooksViewSet
- lets say word-in-book:
@detail_route(method=['GET'], url_path='word-in-book')
def word_in_book(self, request, *args, **kwargs):
object = self.get_object()
queryset = object.wordinbook_set.all()
page = self.paginate_queryset(queryset)
if page is not None:
serializer = WordInBookSerializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
In this way you will get the additional endpoint:
/books/1/word-in-book/
which will return you the paginated results of word in books models.
Hope this helps.
I believe that pagination in your case is not possible - you can just transform code a little to return lets say: 10 first objects.