Jeremias Enriquez
Jeremias Enriquez

Reputation: 129

Filtering on action decorator - Django Rest Framework

I am trying to filter data using a decorator action on Django Rest Framework, it works perfect if I use the global queryset (get_queryset() function) but I need to use it in a separate function.

I am using django-filter to perform it. This is the code.

My view:

class ShippingAPI(viewsets.ModelViewSet):
    serializer_class = ShippingSerializer
    filter_backends = (DjangoFilterBackend,)
    filter_fields = ('origin__department', 'destination__department', 'first_collection_date', 'last_collection_date', 'vehicle')

The override (action)

@action(detail=False, methods=['GET'])
def filter_shippings(self, request, **kwargs):
    queryset = Shipping.objects.filter(status=2, orderStatus=0)
    serializer = SearchShippingSerializer(queryset, many=True) #Yes, I am using another serializer, but it is solved,I use diferent if it is necesary
    return Response(serializer.data)

After use my url 'api/filter_shipping/(all filters here)', this still return all the data without the filters I am requesting.

Thanks for your help

Upvotes: 10

Views: 17042

Answers (4)

Mohammed Sunasra
Mohammed Sunasra

Reputation: 170

I think the filter integration works correctly if you pass the query params along with your action in the URL. Below was the action that I had

@action(methods=['GET'], detail=False)
def export(self, request): 
    queryset = self.get_queryset()
    filtered_queryset = self.filter_queryset(queryset)

When trying to call the export action from DRF browsable API, the request that was getting sent was /api/viewname/export/ instead, it should called like
/api/viewname/export/?query_param_1=value1&?query_param_2=value2

Passing the query params along with the action will call the filterset class and hence you will get a filtered queryset in filtered_queryset variable

Commented on the Github issue as well: https://github.com/carltongibson/django-filter/issues/967#issuecomment-828220562

Upvotes: 10

jplattus
jplattus

Reputation: 341

Add filter_queryset function like this. It worked for me. Found the solution in Django-filters issues: https://github.com/carltongibson/django-filter/issues/967 .

@action(detail=False, methods=['GET'])
def filter_shippings(self, request, **kwargs):
    queryset = self.filter_queryset(self.get_queryset()).filter(status=2, orderStatus=0)
    serializer = SearchShippingSerializer(queryset, many=True) #Yes, I am using another serializer, but it is solved,I use diferent if it is necesary
    return Response(serializer.data)

Upvotes: 13

Gabriel Muj
Gabriel Muj

Reputation: 3805

You could override the normal get_queryset method from the view and check self.action

class ShippingAPI(viewsets.ModelViewSet):
    queryset = Shipping.objects.all()
    serializer_class = ShippingSerializer
    filter_backends = (DjangoFilterBackend,)
    filter_fields = ('origin__department', 'destination__department', 'first_collection_date', 'last_collection_date', 'vehicle')

    def get_queryset(self):
        queryset = super().get_queryset()

        if self.action == 'filter_shippings':
            queryset = queryset.filter(status=2, orderStatus=0)
        elif self.action == 'other_action':
            queryset = queryset.filter(...)  # other action filter

        return queryset

Upvotes: 4

schillingt
schillingt

Reputation: 13731

You can filter on the result of get_queryset to limit your results.

@action(detail=False, methods=['GET'])
def filter_shippings(self, request, **kwargs):
    queryset = self.get_queryset().filter(status=2, orderStatus=0)
    serializer = SearchShippingSerializer(queryset, many=True) #Yes, I am using another serializer, but it is solved,I use diferent if it is necesary
    return Response(serializer.data)

Edit: You can create a custom filter do the filtering as needed. Here is the example from django-filter's docs.

import django_filters

class ProductFilter(django_filters.FilterSet):
    class Meta:
        model = Product
        fields = ['name', 'price', 'manufacturer']

def product_list(request):
    filter = ProductFilter(request.GET, queryset=Product.objects.all())
    return render(request, 'my_app/template.html', {'filter': filter})

Upvotes: 6

Related Questions