Django Rest-Framework nested serializer order

29,225

Solution 1

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

Solution 2

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']

Solution 3

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')))

Solution 4

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
Share:
29,225

Related videos on Youtube

laa
Author by

laa

Updated on February 20, 2020

Comments

  • laa
    laa about 4 years

    Is there a way to order a nested serializer _set, for example order by pk or time-stamp.

    So basically order song_set shown in the json data below from the most recent to the latest object created, in this case by order_by('-timestamp') or order_by('-pk').

    Json data

    {
        "pk": 151,
        "album_name": "Name",
        "song_set": [
             {
               pk: 3,
               timestamp: '5 seconds'
             },
             {
               pk: 2,
               timestamp: '10 seconds'
             },
             {
               pk: 1,
               timestamp: '15 seconds'
             }
        ]
    }
    

    Model

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

    Serializer

    class SongListSerializer(HyperlinkedModelSerializer):
        class Meta:
            model = Song
            fields = [
                'pk',
                'timestamp'
            ]
    
    class AlbumSerializer(HyperlinkedModelSerializer):
        song_set = SongListSerializer(many=True, read_only=True)
        class Meta:
            model = Album
            fields = [
                'pk',
                'timestamp',
                'song_set'
            ]
    
  • Jiss Raphel
    Jiss Raphel over 5 years
    How can I pass a variable to filter the values with a field?
  • Muhammad Hassan
    Muhammad Hassan over 5 years
    You can pass data in serializer context.
  • Jiss Raphel
    Jiss Raphel over 5 years
    I did that way, but was thinking like If I can pass the set of filtered instances along with the data like "AlbumSerializer(data,song_instances)" this is wrong but is ther ea similar way to directly pass instances to nested list serializer without using serializermethod()?
  • Muhammad Hassan
    Muhammad Hassan over 5 years
    If you do not want to filter your data on the basis of your instance, than you can pass filtered instances to you serializer. If tou want to pass data directly to your nested serialized, than that data must be part of your queryset.
  • Saad Aleem
    Saad Aleem about 5 years
    I'm sure there could be a better way to do this. I think changing the order through the ModelManager could be a better approach. Not sure about it, though.
  • MooCow
    MooCow over 4 years
    This looks like it could have run time performance impacts since sorting seems to be left to the server and the not the database.
  • Pransh Tiwari
    Pransh Tiwari almost 4 years
    This saved a lot of effort! Thanks
  • 7ball
    7ball about 3 years
    This is the better solution
  • Yeonghun
    Yeonghun almost 3 years
    This solved my issue when i didn't want to adopt the SerializerMethodField solution
  • Oscar
    Oscar over 2 years
    Great solution!