Reputation: 453
Our site is DRF backend, with Vue frontend. It incorporates extensive filter options, so any endpoint could have many query parameters - like example.com?name__icontains=bob&...etc.
We have a situation on one view where we need to return a sum of one column. Instead of creating a separate endpoint for the frontend to hit, we want to add it to the results of the original view. Like so...
{
"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
}
}
My first attempt was to add this to the get()
method of the view:
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
...which worked when I did the full (unfiltered) list. But when I added filters in the URL, the total was always the same. That made me realize that get_queryset()
doesn't add query parameters from the URL. I then realized that the pagination class must be incorporating query parameters, so my second solution was to add this code to our custom pagination class. That works, but it seems like a hack.
What's the best way to get the queryset with query parameters already added, or to add them myself? I know those parameters are available in kwargs, so if (for example) my view only filtered on "name", I could access the filter via kwargs['name']
- simple. But, as I said, there can be many filter args, and they certainly vary from one view to another.
If I loop through kwargs, I'm thinking I might be getting other things besides filter args - sort order comes to mind - but I'm guessing there may be others. I need a fool-proof way of getting ALL filter args and ONLY filter args.
Upvotes: 3
Views: 1632
Reputation: 21812
I assume you are using some generic view or are atleast inheriting from ListModelMixin
. The get_queryset
method does not do the filtering of the queryset, that task is done by the filter_queryset
method of the view, hence when you override the get
method and don't call that method there is no filtering done. Change your code to use it like so:
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
For understanding better exactly how the builtin view works you might want to look at it's source code [GitHub].
Upvotes: 1
Reputation: 238
Create empty context
Context = {}
Update context with each existing filter in request.GET
If request.GET.get('filter'):
Context['filter] = request.GET.get('filter')
Add filters to queryset
Query.objects.filter(**Context )
Upvotes: 0