User Guide
Reference Documentation
Developer Documentation
Django-filter provides a simple way to filter down a queryset based onparameters a user provides. Say we have aProduct
model and we want to letour users filter which products they see on a list page.
Note
If you’re using django-filter with Django Rest Framework, it’s recommendedthat you read theIntegration with DRF docs after this guide.
Let’s start with our model:
fromdjango.dbimportmodelsclassProduct(models.Model):name=models.CharField(max_length=255)price=models.DecimalField(max_digits=5,decimal_places=2)description=models.TextField()release_date=models.DateField()manufacturer=models.ForeignKey(Manufacturer,on_delete=models.CASCADE)
We have a number of fields and we want to let our users filter based on thename, the price or the release_date. We create aFilterSet
for this:
importdjango_filtersclassProductFilter(django_filters.FilterSet):name=django_filters.CharFilter(lookup_expr='iexact')classMeta:model=Productfields=['price','release_date']
As you can see this uses a very similar API to Django’sModelForm
. Justlike with aModelForm
we can also override filters, or add new ones using adeclarative syntax.
The declarative syntax provides you with the most flexibility when creatingfilters, however it is fairly verbose. We’ll use the below example to outlinethecore filter arguments on aFilterSet
:
classProductFilter(django_filters.FilterSet):price=django_filters.NumberFilter()price__gt=django_filters.NumberFilter(field_name='price',lookup_expr='gt')price__lt=django_filters.NumberFilter(field_name='price',lookup_expr='lt')release_year=django_filters.NumberFilter(field_name='release_date',lookup_expr='year')release_year__gt=django_filters.NumberFilter(field_name='release_date',lookup_expr='year__gt')release_year__lt=django_filters.NumberFilter(field_name='release_date',lookup_expr='year__lt')manufacturer__name=django_filters.CharFilter(lookup_expr='icontains')classMeta:model=Productfields=['price','release_date','manufacturer']
There are two main arguments for filters:
field_name
: The name of the model field to filter on. You can traverse“relationship paths” using Django’s__
syntax to filter fields on arelated model. ex,manufacturer__name
.
lookup_expr
: Thefield lookup to use when filtering. Django’s__
syntax can again be used in order to support lookup transforms.ex,year__gte
.
Together, the fieldfield_name
andlookup_expr
represent a complete Djangolookup expression. A detailed explanation of lookup expressions is provided inDjango’slookup reference. django-filter supports expressions containingboth transforms and a final lookup.
The FilterSet Meta class provides afields
attribute that can be used foreasily specifying multiple filters without significant code duplication. Thebase syntax supports a list of multiple field names:
importdjango_filtersclassProductFilter(django_filters.FilterSet):classMeta:model=Productfields=['price','release_date']
The above generates ‘exact’ lookups for both the ‘price’ and ‘release_date’fields.
Additionally, a dictionary can be used to specify multiple lookup expressionsfor each field:
importdjango_filtersclassProductFilter(django_filters.FilterSet):classMeta:model=Productfields={'price':['lt','gt'],'release_date':['exact','year__gt'],}
The above would generate ‘price__lt’, ‘price__gt’, ‘release_date’, and‘release_date__year__gt’ filters.
Note
The filter lookup type ‘exact’ is an implicit default and therefore neveradded to a filter name. In the above example, the release date’s exactfilter is ‘release_date’, not ‘release_date__exact’. This can be overriddenby the FILTERS_DEFAULT_LOOKUP_EXPR setting.
Items in thefields
sequence in theMeta
class may include“relationship paths” using Django’s__
syntax to filter on fields on arelated model:
classProductFilter(django_filters.FilterSet):classMeta:model=Productfields=['manufacturer__country']
Likedjango.contrib.admin.ModelAdmin
, it is possible to overridedefault filters for all the models fields of the same kind usingfilter_overrides
on theMeta
class:
classProductFilter(django_filters.FilterSet):classMeta:model=Productfields={'name':['exact'],'release_date':['isnull'],}filter_overrides={models.CharField:{'filter_class':django_filters.CharFilter,'extra':lambdaf:{'lookup_expr':'icontains',},},models.BooleanField:{'filter_class':django_filters.BooleanFilter,'extra':lambdaf:{'widget':forms.CheckboxInput,},},}
TheFilterSet
may be initialized with an optionalrequest
argument. Ifa request object is passed, then you may access the request during filtering.This allows you to filter by properties on the request, such as the currentlylogged-in user or theAccepts-Languages
header.
Note
It is not guaranteed that arequest will be provided to theFilterSetinstance. Any code depending on a request should handle theNone case.
.qs
¶To filter the primary queryset by therequest
object, simply override theFilterSet.qs
property. For example, you could filter blog articles to onlythose that are published and those that are owned by the logged-in user(presumably the author’s draft articles).
classArticleFilter(django_filters.FilterSet):classMeta:model=Articlefields=[...]@propertydefqs(self):parent=super().qsauthor=getattr(self.request,'user',None)returnparent.filter(is_published=True) \|parent.filter(author=author)
ModelChoiceFilter
¶Thequeryset
argument forModelChoiceFilter
andModelMultipleChoiceFilter
supports callable behavior. If a callable is passed, it will be invoked with therequest
as its only argument. This allows you to perform the same kinds ofrequest-based filtering without resorting to overridingFilterSet.__init__
.
defdepartments(request):ifrequestisNone:returnDepartment.objects.none()company=request.user.companyreturncompany.department_set.all()classEmployeeFilter(filters.FilterSet):department=filters.ModelChoiceFilter(queryset=departments)...
Filter.method
¶You can control the behavior of a filter by specifying amethod
to performfiltering. View more information in themethod reference.Note that you may access the filterset’s properties, such as therequest
.
classF(django_filters.FilterSet):username=CharFilter(method='my_custom_filter')classMeta:model=Userfields=['username']defmy_custom_filter(self,queryset,name,value):returnqueryset.filter(**{name:value,})
Now we need to write a view:
defproduct_list(request):f=ProductFilter(request.GET,queryset=Product.objects.all())returnrender(request,'my_app/template.html',{'filter':f})
If a queryset argument isn’t provided then all the items in the default managerof the model will be used.
If you want to access the filtered objects in your views, for example if youwant to paginate them, you can do that. They are in f.qs
We need a URL pattern to call the view:
path('list/',views.product_list,name="product-list")
And lastly we need a template:
{% extends "base.html" %}{% block content %} <form method="get"> {{ filter.form.as_p }} <input type="submit" /> </form> {% for obj in filter.qs %} {{ obj.name }} - ${{ obj.price }}<br /> {% endfor %}{% endblock %}
And that’s all there is to it! Theform
attribute contains a normalDjango form, and when we iterate over theFilterSet.qs
we get the objects inthe resulting queryset.
In addition to the above usage there is also a class-based generic viewincluded in django-filter, which lives atdjango_filters.views.FilterView
.You must provide either amodel
orfilterset_class
argument, similar toListView
in Django itself:
# urls.pyfromdjango.urlsimportpathfromdjango_filters.viewsimportFilterViewfrommyapp.modelsimportProducturlpatterns=[path("list/",FilterView.as_view(model=Product),name="product-list"),]
If you provide amodel
optionally you can setfilterset_fields
to specify a list or a tuple ofthe fields that you want to include for the automatic construction of the filterset class.
You must provide a template at<app>/<model>_filter.html
which gets thecontext parameterfilter
. Additionally, the context will containobject_list
which holds the filtered queryset.
A legacy functional generic view is still included in django-filter, althoughits use is deprecated. It can be found atdjango_filters.views.object_filter
. You must provide the same argumentsto it as the class based view:
# urls.pyfromdjango.urlsimportpathfromdjango_filters.viewsimportobject_filterfrommyapp.modelsimportProducturlpatterns=[path("list/",object_filter,{'model':Product},name="product-list"),]
The needed template and its context variables will also be the same as theclass-based view above.