Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Paginate relationship in Django REST Framework?

We are using Django REST Framework for our API and we have a need to paginate relationship fields that return multiple items.

To demonstrate using examples similar to those in the documentation:

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ('order', 'title')

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True)

    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')

Example serialized output for an Album:

{
    'album_name': 'The Grey Album',
    'artist': 'Danger Mouse'
    'tracks': [
        {'order': 1, 'title': 'Public Service Annoucement'},
        {'order': 2, 'title': 'What More Can I Say'},
        {'order': 3, 'title': 'Encore'},
        ...
    ],
}

This becomes problematic where there are say hundreds of tracks in the Album. Is there a way to paginate the 'tracks' in this case?

Ideally, I know that in cases like this, the 'tracks' should maybe point to an API URL that just returns the Tracks for a particular Album - which in turn can be paginated easily. The down side to that approach being the extra request (and hence delay, etc) required to get even the first few tracks. In our case, its important that we be able to get at least a few of the Tracks with the single request to the Album API and then dynamically load the rest of the tracks as and when required.

Does the DRF offer any specific feature or pattern for this? Or are there any work arounds?

like image 849
Deepak Prakash Avatar asked Mar 25 '13 14:03

Deepak Prakash


People also ask

Can we do Pagination for data in rest framework?

REST framework includes support for customizable pagination styles. This allows you to modify how large result sets are split into individual pages of data.

How does django deal with Pagination?

Note that you can give Paginator a list/tuple, a Django QuerySet , or any other object with a count() or __len__() method. When determining the number of objects contained in the passed object, Paginator will first try calling count() , then fallback to using len() if the passed object has no count() method.

What is HyperlinkedModelSerializer?

HyperlinkedModelSerializer is a layer of abstraction over the default serializer that allows to quickly create a serializer for a model in Django. Django REST Framework is a wrapper over default Django Framework, basically used to create APIs of various kinds.

What is Pagination programming?

Pagination is a process that is used to divide a large data into smaller discrete pages, and this process is also known as paging. Pagination is commonly used by web applications and can be seen on Google.


2 Answers

Answer copied from Tom's link above in case of future bit rot:

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ('order', 'title')

class PaginatedTrackSerializer(pagination.PaginationSerializer):
    class Meta:
        object_serializer_class = TrackSerializer

class AlbumSerializer(serializers.ModelSerializer):

    tracks = serializers.SerializerMethodField('paginated_tracks')


    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')


    def paginated_tracks(self, obj):
        paginator = Paginator(obj.tracks.all(), 10)
        tracks = paginator.page(1)

        serializer = PaginatedTrackSerializer(tracks)
        return serializer.data
like image 52
Malcolm Box Avatar answered Sep 18 '22 16:09

Malcolm Box


Since DRF 3.1, PaginationSerializer is not supported. Here's solution.


settings.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 5
}

serializers.py

from myapp.models import Album, Track
from rest_framework import pagination, serializers

class AlbumSerializer(serializers.HyperlinkedModelSerializer):
    tracks = serializers.SerializerMethodField('paginated_tracks')

    class Meta:
        model = Album

    def paginated_tracks(self, obj):
        tracks = Track.objects.filter(album=obj)
        paginator = pagination.PageNumberPagination()
        page = paginator.paginate_queryset(tracks, self.context['request'])
        serializer = TrackSerializer(page, many=True, context={'request': self.context['request']})
        return serializer.data

class TrackSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Track

OR you can substitute def paginated_tracks for

from rest_framework.settings import api_settings

    def get_paginated_tracks(self, obj):
        tracks = Track.objects.filter(album=obj)[:api_settings.PAGE_SIZE]
        serializer = TrackSerializer(tracks, many=True, context={'request': self.context['request']})
        return serializer.data

It even requires one less queries than above.

like image 21
Chemical Programmer Avatar answered Sep 22 '22 16:09

Chemical Programmer