Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 610
nbeuchat Avatar asked Mar 01 '18 23:03

nbeuchat


People also ask

What is the purpose of filter () method in Django?

The filter() method is used to filter you search, and allows you to return only the rows that matches the search term.

Can you filter by property Django?

Nope. Django filters operate at the database level, generating SQL. To filter based on Python properties, you have to load the object into Python to evaluate the property--and at that point, you've already done all the work to load it.


1 Answers

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)
like image 153
JPG Avatar answered Oct 09 '22 09:10

JPG