首页 > 解决方案 > Django 过滤视图集,需要对所有过滤行进行注释。按“全部”分组?

问题描述

这里有数百个关于各种 django 注释/聚合结构和过滤器的问题,但我找不到这个简单的用例被问(或回答)。

我有一个“Payments”模型和关联的 ListAPIView ViewSet 端点以及很好的设置 DjangoFilters,因此客户端可以过滤 created__lte、created__gte、company= 等。

现在我想添加一个从上面派生的端点,但只返回一些字段的总和和过滤对象的总数。

如果我只是“从头开始”编写视图(我可以将 DRF 视图破解为执行 get_queryset().aggregate() 然后输入序列化程序并返回),我确切地知道如何做到这一点,但我想这样做如果可能的话,以“Django 方式”。

例如,结合定义“total_amount”和“nbr”的序列化程序,这(几乎)有效:

queryset = models.Payment.objects.values('company').annotate(total_amount=Sum('amount'),
                                                             nbr=Count('id'))

values() 按“company”(付款的子字段)调用分组,结合 annotate() 对所有公司付款执行求和,并使用 total_amount/nbr 进行注释。添加过滤查询参数可以神奇地调整注释中的内容。

问题是,如果我不想按“公司”分组(甚至过滤),我只想“按所有人分组”怎么办?有没有办法做到这一点?

我意识到这已经有点神奇了,但据我所知,Django 式的注释分组方式是这样的。

我也意识到我可能真的更好,只是通过劫持 .retrieve() 来评估查询集,最后加上 .aggregate() 并即时创建响应......虽然仍然很好奇:)

标签: djangodjango-rest-framework

解决方案


我最终得到了“hack”,在视图中使用 .aggregate 调用覆盖 list(),并通过序列化程序将其打包以用于响应。这是我能想到的最规范的方式(我的意思是,尽可能多地重用 Django/DRF 的移动部分,例如查询集的自动过滤、序列化等)。

奖励:注意需要的 Coalesce() 包装,因为如果集合为空,Sum() 不会返回 0。

def list(self, request, *args, **kwargs):
    queryset = self.filter_queryset(self.get_queryset())
    stats = queryset.aggregate(total_amount=Coalesce(Sum('amount'), 0),
                               total_vat=Coalesce(Sum('vat'), 0),
                               nbr_payments=Count('id'))
    # .aggregate() returns a dict of the results, not a QuerySet. Wrap it 
    # into a response through the serializer.
    sclass = self.get_serializer_class()
    return Response(sclass(stats).data)

推荐阅读