Django отфильтрованный Viewset, необходимо аннотировать сумму по всем отфильтрованным строкам. Группировать по "всем"? - PullRequest
0 голосов
/ 04 мая 2020

Здесь сотни вопросов по различным django аннотациям / агрегатным конструкциям и фильтрам, но я не смог найти этот простой вариант использования, заданный (или отвеченный).

У меня есть модель "Платежи" и связанную конечную точку ListAPIView ViewSet с хорошо настроенными DjangoFilters, чтобы клиент мог фильтровать по созданному_лите, созданному_гте, компании = et c.

Теперь я хочу добавить конечную точку, которая получается из вышеупомянутого, но возвращает только сумму некоторые поля и количество отфильтрованных объектов.

Я точно знаю, как это сделать, если я просто напишу представление "с нуля" (я могу просто взломать представление DRF для выполнения get_queryset (). aggregate ( ) затем вводится в сериализатор и возвращается), но я хочу сделать это "Django -way", если это возможно.

Например, в сочетании с сериализатором, который определяет "total_amount" и "nbr" , это (почти) работает:

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

Группы значений () вызывают "компания" (подполе "Оплата"), что в сочетании с annotate () pe формирует сумму по всем платежам компании и комментирует total_amount / nbr. Добавление параметров запроса фильтрации волшебным образом корректирует то, что входит в аннотацию.

Проблема в том, что если я не хочу группировать (или даже фильтровать) по «компании», я просто хочу «группировать по всем» «? Есть ли способ сделать это?

Я понимаю, что это уже немного волшебно, но, насколько я знаю, способ создания группы для аннотации - Django.

I также понимаю, что мне, вероятно, действительно лучше, просто взломав .retrieve () для оценки набора запросов с помощью .aggregate (), прикрепленного в конце, и на лету, создавая ответ ... все же любопытно:)

1 Ответ

0 голосов
/ 05 мая 2020

В результате я получил "hack", переопределяющий список () в View с вызовом .aggregate и упаковал его для ответа через сериализатор. Это был самый канонический способ, который я мог выяснить (я имею в виду повторное использование как можно большего количества движущихся частей Django / DRF, таких как автоматическая c фильтрация набора запросов, сериализация et c).

Бонус: обратите внимание на перенос 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)
...