Django Rest-Framework nested serializer order
Question:
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'
]
Answers:
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
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
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')))
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']
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'
]
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
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
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')))
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']