简体   繁体   English

如何在DRF中添加嵌套关系的平均值

[英]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) 如何向Musician模型添加新字段avg_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, 一种简单有效的解决方案是get_queryset()方法中将QuerySet注释为:

from django.db.models import Avg, F


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

    def get_queryset(self): return super().get_queryset().annotate(avg_rating=Avg(F('album__num_stars')))


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

    class Meta:
        fields = '__all__'
        model = Musician

try this code by adding SerializerMethodField in serializer class 通过在序列化器类中添加SerializerMethodField尝试此代码

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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