Florian
Florian

Reputation: 773

DRF use nested serializer to create key object instead of array

Context

Say we take this example from the DRF relations guide.

# models.py
class Album(models.Model):
    album_name = models.CharField(max_length=100)
    artist = models.CharField(max_length=100)

class Track(models.Model):
    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
    order = models.IntegerField()
    title = models.CharField(max_length=100)
    duration = models.IntegerField()

    class Meta:
        unique_together = ('album', 'order')
        ordering = ['order']

    def __str__(self):
        return '%d: %s' % (self.order, self.title)

Using a serializer will get us this output

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

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

Will get us this output:

{
    'album_name': 'Things We Lost In The Fire',
    'artist': 'Low',
    'tracks': [
        '1: Sunflower',
        '2: Whitetail',
        '3: Dinosaur Act',
        ...
    ]
}

Question

How can i use the serializer to get the output like this:

  {
    'album_name': 'Things We Lost In The Fire',
    'artist': 'Low',
    'tracks': {
        1: {order: 1, title: 'Sunflower'},
        2: {order:2, title: 'Whitetail'},
        3: {order:3, title: 'Dinosaur Act'},
        ...
    }
}

This way we have an object with tracks instead of a numeric array. So i can do this.props.album.tracks[2].title this instead of this.props.album.tracks.find(track => track.order == 2}).title in javascript.

``I have an use case in where this seems to be more convenient in Reactjs.

What i have tried

I thought about overriding the to_representation method. But i see that this will get me an recursive loop.

class TrackSerializer(serializers.ModelSerializer):
    def to_representation(self, instance):
        print(self)
        return '%s: { %s }' % (instance.order, self.to_representation(instance))
    class Meta:
        fields = '__all__'
        model = Track

Furthermore i have searched and read the docs pretty well. But didn't find any solution for what i think should be a pretty logical solution to have out of the box. Making me think that i am wrong and missing something.

Thanks in advance.

Upvotes: 0

Views: 968

Answers (1)

JPG
JPG

Reputation: 88659

Define a new TrackSerializer and use it in AlbumSerializer class as,


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


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

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

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        representation['tracks'] = {track['id']: track for track in representation['tracks']}
        return representation

Upvotes: 1

Related Questions