Skip to content
thesarfo

Note

Filtering a ViewSet by Query Param

Overriding get_queryset() on a viewset to filter by a query string parameter like collection_id.

views 0

Currently, when we hit the products endpoint (127.0.0.1:8000/store/products), we get all products in our database. Now what if we want to filter our products, lets say filter them by a specific collection.

We should be able to pass a querystring parameter indicating the collection_id and if we set the collection_id to 1 we should only see products in that collection. (127.0.0.1:8000/store/products?collection_id=1)

How can we implement this: This is our current product viewset.

class ProductViewSet(ModelViewSet):
queryset = Product.objects.all() # we will remove this line
serializer_class = ProductSerializer
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)

Now in order for us to be able to pass a query param to our url, we need to override the queryset attribute by creating a method for it.

class ProductViewSet(ModelViewSet):
serializer_class = ProductSerializer
def get_queryset(self):
queryset = Product.objects.all()
collection_id = self.request.query_params['collection_id'] # query parameter
if collection_id is not None:
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)

If we do this implementation, django rest framework wont be able to find out the base url for our products endpoint so we have to explicitly set it in our urls.py file. see below

router.register('products', views.ProductViewSet, basename='products') #added the basename as a third param