Serenity
Serenity

Reputation: 4054

django filter on APIView

I have a APIView class for showing all the rents and posting and delete etc. Now i want search feature so i tried to use DjangoFilterBackend but it is not working. I see in documentation, it has been used with ListAPIView but how can i use it in APIView.

class Rent(APIView):
    """
    List all the rents if token is not provided else a token specific rent
    """
    serializer_class = RentSerializer
    filter_backends = (DjangoFilterBackend,)
    filter_fields = ('city', 'place', 'property_category',)
    search_fields = ('=city', '=place')
    def get(self, request, token=None, format=None):
        reply={}
        try:
            rents = Rental.objects.all()
            if token:
                rent = Rental.objects.get(token=token)
                reply['data'] = self.serializer_class(rent).data
            else:
                reply['data'] = self.serializer_class(rents, many=True).data
        except Rental.DoesNotExist:
            return error.RequestedResourceNotFound().as_response()
        except:
            return error.UnknownError().as_response()
        else:
            return Response(reply, status.HTTP_200_OK)

when i search the rent with the following parameters in the url, i get all the rents, instead i should get only those rents that lies in city Kathmandu and place koteshwor

http://localhost:8000/api/v1/rents?city=Kathmandu&place=Koteshwor

Upvotes: 17

Views: 19720

Answers (4)

saran3h
saran3h

Reputation: 14052

In case someone is wondering how can we integrate django_filters filter_class with api_views:

@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def filter_data(request, format=None):
    qs = models.YourModal.objects.all()

    filtered_data = filters.YourFilter(request.GET, queryset=qs)
    filtered_qs = filtered_data.qs
    ....
    return response.Ok(yourData)

Upvotes: 9

Braden Holt
Braden Holt

Reputation: 1594

Adding to @ChidG's answer. All you need to do is override the DjangoFilterBackend's filter_queryset method, which is the entry point for the filter, and pass it the instance of your APIView. The important point to note here is you must declare filter_fields or filter_class on the view in order to get the filter to work. Otherwise it just return your queryset unfiltered.

If you're more curious about how this works, the class is located at django_filters.rest_framework.backends.py

In this example, the url would look something like {base_url}/foo?is_active=true

from django_filters.rest_framework import DjangoFilterBackend

class FooFilter(DjangoFilterBackend):

    def filter_queryset(self, request, queryset, view):
        filter_class = self.get_filter_class(view, queryset)

        if filter_class:
            return filter_class(request.query_params, queryset=queryset, request=request).qs
        return queryset

class Foo(APIView):

    permission_classes = (AllowAny,)
    filter_fields = ('name', 'is_active')

    def get(self, request, format=None):
        queryset = Foo.objects.all()

        ff = FooFilter()
        filtered_queryset = ff.filter_queryset(request, queryset, self)

        if filtered_queryset.exists():
            serializer = FooSerializer(queryset, many=True)
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response([], status=status.HTTP_200_OK)

Upvotes: 3

itzMEonTV
itzMEonTV

Reputation: 20349

Here If you are using APIView, There is nothing to do with filters.So you have to do like

get_data = request.query_params #or request.GET check both

Then

Rental.objects.filter(city=get_data['city'], place=get_data['place'])

Upvotes: 11

ChidG
ChidG

Reputation: 3223

To use the functionality of DjangoFilterBackend, you could incorporate the filter_queryset method from GenericViewSet, which is the DRF class that inherits from APIView and leads to all specific 'generic' view classes in DRF. It looks like this:

def filter_queryset(self, queryset):
    """
    Given a queryset, filter it with whichever filter backend is in use.
    You are unlikely to want to override this method, although you may need
    to call it either from a list view, or from a custom `get_object`
    method if you want to apply the configured filtering backend to the
    default queryset.
    """
    for backend in list(self.filter_backends):
        queryset = backend().filter_queryset(self.request, queryset, self)
    return queryset

https://github.com/encode/django-rest-framework/blob/master/rest_framework/generics.py

Upvotes: 16

Related Questions