简体   繁体   中英

How to add average of nested relation in DRF

Question
How can I add a new field avg_rating to Musician model which represents the avg of their albums rating (sum_of_rating / num_of_albums_in DB)

I have two models,

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)


class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

and serializer as,

class AlbumSerializer(serializers.ModelSerializer):
    artist = serializers.StringRelatedField()

    class Meta:
        fields = '__all__'
        model = Album


class MusicianSerializer(serializers.ModelSerializer):
    albums = AlbumSerializer(many=True, source='album_set')

    class Meta:
        fields = '__all__'
        model = Musician

views,

class AlbumViewset(ModelViewSet):
    serializer_class = AlbumSerializer
    queryset = Album.objects.all()


class MusicianViewset(ModelViewSet):
    serializer_class = MusicianSerializer
    queryset = Musician.objects.all()

and I got respons as,

[
    {
        "id": 1,
        "albums": [
            {
                "id": 1,
                "artist": "Musician object",
                "name": "Scorpion",
                "release_date": "2018-07-24",
                "num_stars": 4
            },
            {
                "id": 2,
                "artist": "Musician object",
                "name": "More Life",
                "release_date": "2017-07-24",
                "num_stars": 4
            },
            {
                "id": 3,
                "artist": "Musician object",
                "name": "Views",
                "release_date": "2017-07-24",
                "num_stars": 3
            },
            {
                "id": 4,
                "artist": "Musician object",
                "name": "Take Care",
                "release_date": "2011-07-24",
                "num_stars": 5
            }
        ],
        "first_name": "Drake",
        "last_name": "Graham",
        "instrument": "Sitar"
    },
    {
        "id": 2,
        "albums": [
            {
                "id": 5,
                "artist": "Musician object",
                "name": "Voicenotes",
                "release_date": "2018-07-24",
                "num_stars": 5
            },
            {
                "id": 6,
                "artist": "Musician object",
                "name": "Nine Track Mind",
                "release_date": "2016-07-24",
                "num_stars": 5
            }
        ],
        "first_name": "Charlie",
        "last_name": "Puth",
        "instrument": "singer"
    }
]

One of the easy and efficient solution is annotate the QuerySet in get_queryset() method as,




class MusicianViewset(ModelViewSet):
    serializer_class = MusicianSerializer
    queryset = Musician.objects.all()

    


class MusicianSerializer(serializers.ModelSerializer):
    albums = AlbumSerializer(many=True, source='album_set')
    

    class Meta:
        fields = '__all__'
        model = Musician

try this code by adding SerializerMethodField in serializer class

from django.db.models import Avg, F

 class MusicianSerializer(serializers.ModelSerializer):
    albums = AlbumSerializer(many=True, source='album_set')
    avg_rating = serializers.SerializerMethodField()

    class Meta:
        model = Musician
        fields = '__all__'


    def get_avg_rating(self, obj):
        # for particular musician get all albums and aggregate the all stars and return the avg_rating
        return obj.album_set.aggregate(avgs=Avg(F('num_stars'))).get('avgs',None)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM