Optional filters with Django QuerySets

Sometimes I’ll come across a situation where I have a QuerySet that has some default filters, such as, published products on a website. However, users may also want to include additional optional filters, for example, to filter for products that are on sale.

In this case, how could you create a Django QuerySet that has default filters, such as published=True but can also accept optional filters passed via URL query parameters? For example, something like https://www.example.com?sale=true.

Below is a simplified example of the technique I use for this.

from django.shortcuts import render
from products.models import Product


def products_list(request):
    filters = {'published': True}
    sale = request.GET.get('sale', None)
    if sale == 'true':
        filters['sale'] = True
    products = Product.objects.filter(**filters)
    return render(request, 'products.html', {'products': products})

Basically, you start by creating a dictionary of default filters. Then you check if the request has any expected query parameters, if they do then you append them to your filters dictionary. Finally, you use ** to unpack the dictionary items as keyword arguments into your model manager’s filter() method.

This is a simple example, if you have many possible filters then your view code could become quite long, in which case you may want to abstract all of the if statements into a create_filters() helper function. This could check the request for various expected query parameters and then return a dictionary object that can be unpacked into the filter() method.

Not sure if anyone else has a better way of doing this but just thought I’d share.