haki
haki

Reputation: 9779

adding metadata to filtering in django rest framework

I have a generic ListCreateAPIView view. I've implemented a get_queryset function that performs a search. The function parses the query, extract tags and terms and returns a query set.

def get_queryset(self):
        query = self.request.QUERY_PARAMS.get('query', None)

        # No deleted items
        queryset = Items.objects.filter(deleted__isnull=True)

        if query is None:
            return queryset

        predicates = []

        # Generate predicates from query       

        queryset = queryset.filter(reduce(__and__,predicates))
        return queryset

What is the best way to add metadata to the response with data from the get_queryset function ?

I'm looking for something similar to the way pagination works.

{
 query : {
     terms : ['term1','term2'], 
     tags  : ['tag1','tag2'] , 
    }
 results : [
      { name : 'item1', .... }
      { name : 'item2', .... }
   ]
}

EDIT

So i created a custom FilterBackend for the filtering and I now have an instance of the request and the response. Looking at the pagination code for django rest i see it's wrapping the results in serializer. The pagination is build into the view class so the fw invokes the serialization if a paginator is detected. Looking at the search api did not produce any new ideas.

My question remains, What is the best, and least intrusive way, of adding metadata from the filter backend to the response ?

One way i can think of (and one that i don't like) is to overload the matadata onto the request in the filter backend and override finalize_response in the view - without a doubt the worst way to do it.

Upvotes: 7

Views: 3202

Answers (1)

Fiver
Fiver

Reputation: 10165

I'm not sure it's the best way, but I would probably override get to simply intercept the response object and modify response.data however you wish. Something as simple as

from rest_framework import generics

class SomeModelList(generics.ListCreateAPIView):
    """
    API endpoint representing a list of some things.
    """

    model = SomeModel
    serializer_class = SomeModelSerializer

    def get(self, request, *args, **kwargs):
        response = super(SomeModelList, self).get(request, *args, **kwargs)

        # redefine response.data to include original query params
        response.data = {
            'query': dict(request.QUERY_PARAMS),
            'results': response.data
        }

        return response

If you found yourself repeating this for multiple list views you could keep yourself DRY using a Mixin and include it in your list API classes:

from rest_framework import generics
from rest_framework.mixins import ListModelMixin

class IncludeQueryListMixin(ListModelMixin):
    def list(self, request, *args, **kwargs):
        response = super(IncludeQueryListMixin, self).list(request, *args, **kwargs)

        # redefine response.data to include original query params
        response.data = {
            'query': dict(request.QUERY_PARAMS),
            'results': response.data
        }

        return response


class SomeModelList(IncludeQueryListMixin, generics.ListCreateAPIView):
    """
    API endpoint representing a list of some things.
    """

    model = SomeModel
    serializer_class = SomeModelSerializer

Upvotes: 5

Related Questions