KingFish
KingFish

Reputation: 9153

Django Rest: Serializers Based on Multiple Querysets

I'm working with serializers as described by the link:Serializer relations (section PrimaryKeyRelatedField)

I have a slight different need, I'm sure it's really easy.

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')
    order = models.IntegerField()
    title = models.CharField(max_length=100)
    duration = models.IntegerField()

class SomeWidget(models.Model):
    album = models.ForeignKey(Album)
    track = models.ForeignKey(Track)
    name = models.CharField(max_length=100)
    description = models.CharField(max_length=100)

My need, I need to return the following:

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

I am trying:

class WidgetField(serializers.RelatedField):
    def to_representation(self, value):
        return {
            'id': '1234'
            ....
        }


class TrackListingField(serializers.RelatedField):
    def to_representation(self, value):
        ...

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackListingField(many=True)
    widget = WidgetField()

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

I keep getting the error:

AssertionError: Relational field must provide a `queryset` argument, override `get_queryset`, or set read_only=`True`.

Thanks

Upvotes: 0

Views: 2560

Answers (1)

KingFish
KingFish

Reputation: 9153

Ok, got it. It took a little bit of Googling / trial and error. Apparently you can override the function that returns data for the field.

For example:

class AlbumSerializer(serializers.ModelSerializer):
    widget = serializers.SerializerMethodField()

    def get_widget(self, data):
        return {
            'id': data.id
        }

OR, you can do the following:

class WidgetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Widget
        fields = ('id', 'name', 'description',)

.... and in the AlbumSerializer.get_widget function:

    def get_widget(self, data):
        widget = Widget.objects.get(album=data.album, track=data.track)
        return WidgetSerializer(widget, many=False, context=self.context).data

Finally, you don't have to use the function name "get_widget". You can name it whatever you want. Example:

class AlbumSerializer(serializers.ModelSerializer):
    widget = serializers.SerializerMethodField("fn_override")

    def fn_override(self, data):
        ....

You can follow the pattern in this SO question: Django REST Framework: adding additional field to ModelSerializer

Upvotes: 1

Related Questions