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

时间:2018-08-02 04:48:55

标签: django django-rest-framework

问题
如何为avg_rating模型添加一个新字段Musician来表示其专辑评价的平均值(DB中的sum_of_rating / num_of_albums_in)

我有两个模型,

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()

和序列化器为

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

观看次数

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


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

我得到了回应,

[
    {
        "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"
    }
]

2 个答案:

答案 0 :(得分:0)

一种简单有效的解决方案是注释 QuerySet   get_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

答案 1 :(得分:0)

通过在序列化程序类中添加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)