在Django Rest Framework序列化器中聚合(和其他已注释的)字段

5 浏览
0 Comments

在Django Rest Framework序列化器中聚合(和其他已注释的)字段

我正在寻找一种最佳方式来向DRF(Model)Serializers中添加带注释的字段,如任何聚合(计算)字段。我的场景是简单地情况下,一个端点返回的字段并不存储在数据库中,而是从数据库中计算得出的。

让我们看下面的例子:

models.py

class IceCreamCompany(models.Model):
    name = models.CharField(primary_key=True, max_length=255)
class IceCreamTruck(models.Model):
    company = models.ForeignKey('IceCreamCompany', related_name='trucks')
    capacity = models.IntegerField()

serializers.py

class IceCreamCompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = IceCreamCompany

期望的JSON输出:

[

{

"name": "Pete's Ice Cream",

"total_trucks": 20,

"total_capacity": 4000

},

...

]

我有几个解决方案可以工作,但每个都有一些问题。

选项1:在模型中添加getter并使用SerializerMethodFields

models.py

class IceCreamCompany(models.Model):
    name = models.CharField(primary_key=True, max_length=255)
    def get_total_trucks(self):
        return self.trucks.count()
    def get_total_capacity(self):
        return self.trucks.aggregate(Sum('capacity'))['capacity__sum']

serializers.py

class IceCreamCompanySerializer(serializers.ModelSerializer):
    def get_total_trucks(self, obj):
        return obj.get_total_trucks
    def get_total_capacity(self, obj):
        return obj.get_total_capacity
    total_trucks = SerializerMethodField()
    total_capacity = SerializerMethodField()
    class Meta:
        model = IceCreamCompany
        fields = ('name', 'total_trucks', 'total_capacity')

上述代码可以进行一些重构,但这不会改变这个选项每个IceCreamCompany执行2个额外的SQL查询,这不太高效。

选项2:在ViewSet.get_queryset中进行注释

models.py如上所述。

views.py

class IceCreamCompanyViewSet(viewsets.ModelViewSet):
    queryset = IceCreamCompany.objects.all()
    serializer_class = IceCreamCompanySerializer
    def get_queryset(self):
        return IceCreamCompany.objects.annotate(
            total_trucks=Count('trucks'),
            total_capacity=Sum('trucks__capacity')
        )

这将在单个SQL查询中获取聚合字段,但我不确定如何将它们添加到Serializer中,因为DRF并不会自动知道我在QuerySet中注释了这些字段。如果我将total_trucks和total_capacity添加到serializer中,它将抛出一个关于这些字段在模型上不存在的错误。

选项2可以通过使用View而不是serializer来工作,但如果模型包含很多字段,而只有一部分字段需要在JSON中,那么在不使用serializer的情况下构建端点将是一个相当丑陋的hack。

0