Nested Pagination

Abdelrahmen Ayman
Apr 22, 2018 · 4 min read

Lately, I faced a problem while working on a RESTful API using Django and DRF, my mentor was thinking that it was solvable in one line of code, but then we googled the issue only to find that it had been addressed in several questions on Stackoverflow with no accepted answer, and BOOM that is where the adventure started.

What was the problem?

Actually the problem at first glance seems easy, Paginating a set of objects inside another object. Suppose we have an Author, and a Book with a foreign key to the Author, simple and straight forward. Now, we want to send a response containing the list of authors we are holding in our beloved database, one of the instances is representing a legendary author that has wrote a thousand books, well of course we don’t want the response to display thousand books all at once and leave no space for any other content to be displayed.

Available solutions

At this point we want to do something that is solvable in many ways, the most intutive solutions were:

  • Create a separate endpoint for books and apply pagination to this endpoint.
  • Find a way to paginate books inside the author instance. The first solution is easy, and won’t actually take five minutes to implement, but this is not our goal here, instead, we want to make sure that our code is as clean as possible. Additionally, we wanted the books to be displayed within the author’s view not a separate one. Finally, we have a very challenging mind so we decided that we will go for the second option, yes we love mystery.

Our Solution to the problem

After a decent amount of time I finally found the solution to my problem and I texted my mentor after he was home to celebrate with him wohooo we have done it, I know you don’t care about those details but I am a talkative person so apologies, let’s head to the solution: Firstly we should think, where is the only place that we will be having control on the list of books related to that Author? You’re right, AuthorSerializer Here is our AuthorSerializer:

Pretty simple! Cool, then we should write the function that can access the author and its books inside AuthorSerializer, just a normal function that has nothing different:

Hmmmmm, let’s take this piece of code bit by bit, first the function is expecting 2 arguments self and obj. Firstly, we decided that we want the number of books shown per page is 10:

page_size = 10

Then we used the Paginator found in django.core.paginator to paginate our desired iterable object, which is books list in our case. So we extract the books from obj and pass it to the Paginator constructor along with the page size:

paginator = Paginator(obj.books.all(), page_size)

Now we have in paginator many pages, each page includes 10 instances of Book, that we can access by number like this:

books = paginator.page(1)

After that we serialize the books to make sure they match our BookSerializer restrictions:

serializer = BookSerializer(books, many=True)

And at the end we just return the serialized books to be used by AuthorSerializer. Seems cool but we have missed something, we should actually tell our AuthorSerializer that this function is the one used to serialize books for you, so we have to add one more line to our AuthorSerializer, and we will end up having this:

And we are done, now you can paginate objects inside another object ! Now let’s have a look on some tweaks that we can add to make our code more let’s say dynamic, for now our page_size is fixed to 10 and no way to change but hard coding, so let’s change this a bit, one way that I’ve found suitable to get the page size dynamically is to receive it in the request query params, by doing this we can return a different response in terms of books pagination for each request:

page_size = self.context[‘request’].query_params.get(‘size’) or 10

Also or 10 is used to provide a default value in case size was not passed. We can do the same for requesting which page to access, instead of doing this:

books = paginator.page(1)

we can do this:

page_number = self.context['request'].query_params.get('page') or 1
books = paginator.page(page_number)

Now the Serializer will be like:

Conclusion

Finally, this how we solved the problem we faced, if you’ve faced the same problem and if you’ve found a better solution please let us know and we can update our blog.

Also if you want another similar example you can checkout my answer to this question https://stackoverflow.com/questions/40806879/django-rest-framework-paginate-nested-object/49677960#49677960 and maybe hit an up vote if you like it :D .

Please if you have any questions regarding this topic leave it in the comments below and I’ll be more than happy to answer.

DREIDEV

Software Engineering Business

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store