djangodjango-rest-frameworkdjango-filter

How to sort the results of filtering with django-filter in a ListAPIView


I have a ListAPIView that uses the DjangoFilterBackend to filter rooms based on url parameters. The code below does this just fine.

Now, I would like to sort the results based on a score calculated from other attributes of the Room object, another url parameter, and possibly what we know about the user issuing the request. The function itself does not matter.

How can I sort the results after applying the filter I already have?

If I were to do the filtering myself, I suppose I could do the filtering, calculate the score and sort the results in the get_queryset but I do not know how to do this after the filtering with django-filter.

Example query

For example, I would do this query to filter by price lower than 100. The other_field value would be used to compute the score for sorting:

http://localhost:8000/api/search/rooms?price=100&other_field=200

Code

class RoomFilter(filters.FilterSet):
    price = filters.NumberFilter(name="price", lookup_expr='lte')
    features = filters.ModelMultipleChoiceFilter(
        name="features", 
        conjoined=True,
        queryset=Features.objects.all()
        )

    class Meta:
        model = Room
        fields = ['price', 'features']

class RoomSearchView(generics.ListAPIView):
    queryset = Room.objects.all()
    serializer_class = RoomSearchSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = RoomFilter

Solution

  • Try to override list() in your API as below,

    class RoomSearchView(generics.ListAPIView):
        queryset = Room.objects.all()
        serializer_class = RoomSearchSerializer
        filter_backends = (filters.DjangoFilterBackend,)
        filter_class = RoomFilter
    
        def list(self, request, *args, **kwargs):
            queryset = self.filter_queryset(self.get_queryset())
    
            queryset = queryset.order_by('-id')  # change is here  >> sorted with reverse order of 'id'
    
            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)
    
            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)