Django:如何像在分页中那样将查询参数添加到查询集中?

Django: How to add query params to queryset as is done in Pagination?

我们的网站是DRF后台,Vue前台。它结合了广泛的过滤器选项,因此任何端点都可以有许多查询参数——比如 example.com?name__icontains=bob&...etc.

在一个视图中,我们需要 return 一列的总和。我们不想为前端创建一个单独的端点,而是想将它添加到原始视图的结果中。像这样...

{
    "pagination": {
        ...pagination stuff...
    },
    "results": [
        {
            "id": 1,
            "task": "DMSD-50",
            ...other fields...,
            "duration": 204
        },
        {
            "id": 2,
            "task": "DMSD-53",
            ...other fields...,
            "duration": 121
        },
        ...more rows...
    ],
    "totals": {
        "duration": 955
    }
}

我的第一次尝试是将其添加到视图的 get() 方法中:

    def get(self, request, *args, **kwargs):
        response = super().get(request, *args, **kwargs)
        queryset = self.get_queryset()
        totals = queryset.aggregate(total_duration=Sum('duration'))
        response['totals'] = {
            'duration': totals['total_duration'] or 0
        }
        return response

...在我执行完整(未过滤)列表时有效。但是当我在 URL 中添加过滤器时,总数总是相同的。这让我意识到 get_queryset() 不会添加来自 URL 的查询参数。然后我意识到分页 class 必须合并查询参数,所以我的第二个解决方案是将此代码添加到我们的自定义分页 class。这行得通,但它似乎是一个 hack。

获取已添加查询参数的查询集或自行添加查询集的最佳方法是什么?我知道这些参数在 kwargs 中可用,所以如果(例如)我的视图仅在“名称”上过滤,我可以通过 kwargs['name'] 访问过滤器 - 简单。但是,正如我所说,可以有很多过滤器参数,而且它们肯定因视图而异。

如果我遍历 kwargs,我想我可能会得到除过滤器 args 之外的其他东西 - 想到排序顺序 - 但我猜可能还有其他东西。我需要一种万无一失的方法来获取所有过滤器参数和仅过滤器参数。

  1. 创建空上下文

    Context = {}
    
  2. 使用 request.GET

    中的每个现有过滤器更新上下文
    If request.GET.get('filter'):
    
       Context['filter] = request.GET.get('filter')
    
  3. 向查询集添加过滤器

    Query.objects.filter(**Context )
    

我假设您正在使用一些通用视图或至少继承自 ListModelMixinget_queryset 方法不过滤查询集,该任务由视图的 filter_queryset method 完成,因此当您覆盖 get 方法并且不在那里调用该方法时没有过滤。更改您的代码以像这样使用它:

class YourView(generics.ListAPIView):  # Just an assumption here
    ...
    def get(self, request, *args, **kwargs):
        response = super().get(request, *args, **kwargs)
        queryset = self.filter_queryset(self.get_queryset())
        totals = queryset.aggregate(total_duration=Sum('duration'))
        response['totals'] = {
            'duration': totals['total_duration'] or 0
        }
        return response

为了更好地理解内置视图的工作原理,您可能需要查看它 source code [GitHub]