0

Related to this question I am trying to return data from multiple models in one query by DRF.

class Artist(models.Model):
    artist_name = models.CharField(max_length=100)

class Genre(models.Model):
    genre_name = models.CharField(max_length=100)

class Album(models.Model):
    album_name = models.CharField(max_length=100)
    artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
    genre = models.ForeignKey(Genre, on_delete=nodels.CASCADE)

I want to return a JSON list of all options for albums and genres for all artists.

Something like:

{
    "genres": ["techno", "rap", "rock", ...],
    "albums": ["nevermind", "collection", "fragile", ...]
}

I created a custom serializer:

class InfoSerializer(serializers.Serializer):
    albums = serializers.CharField()
    genres = serializers.CharField()

    class Meta:
        fields = ["albums", "genres"]

and a Viewset:

class OptionsViewSet(ViewSet):
    serializer_class = InfoSerializer

    def list(self, request):
        options = [{"albums": Album.objects.all()},
                   {"genres": Genre.objects.all()}]
        results = InfoSerializer(options, many = True)
        results.is_valid()
        return Response(results.data)

Error message I keep getting:

KeyError when attempting to get value for field albums on serializer InfoSerializer ...

xtlc
  • 1,070
  • 1
  • 15
  • 41

1 Answers1

1

You're giving options to the InfoSerializer, and that serializer assumes that options has an attribute named albums and genres. Since it doesn't have that, it raises an error. See the drf documentation

The design of your viewset looks like you're returning multiple instances but actually what you return is a single dictionary that has only two keys. Albums and genres are independent from each other, so returning an array with many=True doesn't make any sense here.

I'd suggest you return something like this in an APIView. Also you can only retrieve the album_name and genre_name from database which will make this a bit more performant.

{
    "albums": list(Album.objects.all().values_list('album_name', flat=True)),
                   
    "genres": list(Genre.objects.all().values_list('genre_name', flat=True))
}
Cagatay Barin
  • 3,428
  • 2
  • 24
  • 43
  • This works exactly the way I wanted - but now I am not using any serializer. How would the serializer look like if I may ask? Thanks! – xtlc Aug 29 '21 at 20:11