Now we can filter products by collection, now what if we want to filter by another thing, our logic for getting our queryset is gonna get more complicated. This is where we use generic filtering. There is a third party module called django filter that can make us filter any model by any field at all.
first in the terminal install django-filter
pipenv install django-filterThen add django-filter to the list of installed apps.
After that do “from django_filters.rest_framework import DjangoFilterBackend”. The Django filter backend simply lets us set the fields we want to filter by, no matter the field. see below
class ProductViewSet(ModelViewSet): queryset = Product.objects.all() # we brought this back serializer_class = ProductSerializer filter_backends = [DjangoFilterBackend] filterset_fields = ['collection_id'] '''since we have used the DjangoFilterBackend and set the filterset_fields to "collection_id", we are saying that we want to filter by collection_id, so the method that lets us get our queryset can be removed. and then bring back the original queryset'''
'''we get rid of the below function'''
# def get_queryset(self): # queryset = Product.objects.all() # collection_id = self.request.query_params.get('collection_id')
# if collection_id is not None: # queryset = queryset.filter(collection_id=collection_id)
# return queryset
def get_serializer_context(self): return {'request': self.request}
def destroy(self, request, *args, **kwargs): if OrderItem.objects.filter(product_id=kwargs['pk']).count() > 0: return Response({'error': 'Product cannot be deleted because it is associated with an order item'}, status=status.HTTP_405_METHOD_NOT_ALLOWED) return super().destroy(request, *args, **kwargs)The problem with this implementation is that, if we wanted to filter by say the unit price, we will have to add the ‘unit_price’ to the filterset_fields variable. But when we do it that way and we filter by lets say a unit price of 10, it is only going to return the products with the exact unit price of 10. But ideally, we want our unit price to be in a specific range. So we need to find a different implementation.
This is where the FilterSet viewset comes into play. We can create a file called filters.py and use the filterset there. See below
from django_filters.rest_framework import FilterSetfrom .models import Product
class ProductFilter(FilterSet): '''we create a meta class that sets our model to product, and the fields we want to filter it by, we want the collection_id to be exact, and the unit price to be in a range(less than or greater than)''' class Meta: model = Product fields = { 'collection_id': ['exact'], 'unit_price': ['gt', 'lt'], }And then in our views.py, in our ProductViewSet, we can set the filterset_class to the ProductFilter class we created in the filters.py file. and that’ll be it. see below
from .filters import ProductFilter
class ProductViewSet(ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer filter_backends = [DjangoFilterBackend] filterset_class = ProductFilter