[Django]-Django Rest-Framework nested serializer order

70👍

You can use SerializerMethodField and write custom method for this.

class AlbumSerializer(HyperlinkedModelSerializer):
    song_set = serializers.SerializerMethodField()
    class Meta:
        model = Album
        fields = [
            'pk',
            'timestamp',
            'song_set'
        ]

    def get_song_set(self, instance):
        songs = instance.song_set.all().order_by('-timestamp')
        return SongListSerializer(songs, many=True).data

62👍

Add ordering meta parameter to your Song model:

class Song(models.Model):
    album = models.ForeignKey('album.Album', default=1)
    timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)

    class Meta:
        ordering = ['timestamp', 'pk']

31👍

In your ViewSet, you can specify a queryset with a custom Prefetch object that you can filter and order as you like. Prefetching causes just one additional database query (instead of one per parent object when using SerializerMethodField), giving vastly improved performance.

from rest_framework import viewsets
from django.db.models import Prefetch

class AlbumViewSet(viewsets.ModelViewSet):
    queryset = Album.objects.prefetch_related(Prefetch('song_set',
        queryset=Song.objects.order_by('-timestamp')))

7👍

Old thread, but because it’s still popping up on Google I want to share my answer as well. Try overwriting the Serializer.to_representation method. Now you can basically do whatever you want, including customising the sorting of your response. In your case:

class AlbumSerializer(HyperlinkedModelSerializer):
    song_set = SongListSerializer(many=True, read_only=True)
    class Meta:
        model = Album
        fields = [
            'pk',
            'timestamp',
            'song_set'
        ]

    def to_representation(self, instance):
        response = super().to_representation(instance)
        response["song_set"] = sorted(response["song_set"], key=lambda x: x["timestamp"])
        return response

1👍

Another way to solve this is to modify the to_representation method on the ListSerializer to change the ordering when the relationship is queried via the ORM.

This to_representation is similar to how it works for the base ListSerializer in DRF.

class SongListSerializer(HyperlinkedModelSerializer):
    class Meta:
        model = Song
        fields = [
            'pk',
            'timestamp'
        ]

    def to_representation(self, data):
        iterable = data.all().order_by('-timestamp', '-id') \
            if isinstance(data, models.Manager) else data

        return [self.child.to_representation(item) for item in iterable]



class AlbumSerializer(HyperlinkedModelSerializer):
    song_set = SongListSerializer(many=True, read_only=True)
    class Meta:
        model = Album
        fields = [
            'pk',
            'timestamp',
            'song_set'
        ]

Leave a comment