Skip to content

Filter Guide

Filters

Writing filters is similar to the serializer. You can write simple filters just for validating query params or also for filtering if queryset is provided in your view.

Note: If a field is not declared in filter, then no error will be raised and no filtering will be applied on queryset on the basis of that provided filter.

Declaring Filter

Below is the way to declare a simple filter

from djfilters import filters

class MySimpleFilter(filters.Filter):
    id = filters.IntegerField(max_value=100, min_value=1)
    title = filters.CharField(max_length=100, min_length=10)

By default all fields will be not required. This can be changed by providing required=True to the field. Check Field Guide for detailed documentation on all fields.

Validation

This filter will automatically validate id and title according to min, max value/length. What if we want to add custom validation? Its simple. Just define validation methods as below

class MySimpleFilter(filters.Filter):
    id = filters.IntegerField(max_value=100, min_value=1)
    title = filters.CharField(max_length=100, min_length=10)

    def validate(self, attrs):
        # `attrs` is the dict of all validated fields. Cross-field
        # validation goes here.
        if your_check_here:
            raise filters.ValidationError('Your error message here')
        return attrs

    # Or field validation only

    def validate_title(self, title):
        # Write your validation logic here
        if your_check_here:
            raise filters.ValidationError('Your error message here')
        return title

Filtering

Above filter will automatically apply filtering on provided queryset. But for some field there is is need of custom filtering mechanism, it can be done in following way

class MySimpleFilter(filters.Filter):
    id = filters.IntegerField(max_value=100, min_value=1)
    title = filters.CharField(max_length=100, min_length=10)

    def filter(self, validated_data):
        qs = self.queryset.all()
        # write your filtering logic here
        return qs

    def filter_title(self, qs, value):
        return qs.filter(title__icontains=value)

Model Filters

Model Filters are very easy to declare similar to Model Serializer. You just need to add model and fields and you are good to go. Consider following models

from django.db import models
from django.contrib.auth.models import User

class Todo(models.Model):
    title = models.CharField(max_length=100)
    detail = models.TextField()
    is_complete = models.BooleanField(default=False)
    user = models.ForeignKey(to=User, on_delete=models.CASCADE)

Declaring Model Filters

For above model, filter will be declared like this

from djfilters import filters

class TodoFilter(filters.ModelFilter):
    class Meta:
        model = Todo
        fields = '__all__'

By default, all fields in filters will be not required. If you want to change any of the default parameter, you can use extra_kwargs option like this.

class TodoFilter(filters.ModelFilter):
    class Meta:
        model = Todo
        fields = '__all__'
        extra_kwargs = {
            'title': {'required': True, 'lookup_expr': 'icontains'}
        }

Similar to fields, exclude option can also be used to exclude some fields from filters.

Note: Nested filters are not supported so adding depth or any field which is actually a filter will raise an error.

Validation

Custom validation works the same way as for simple filters — define validate(self, attrs) for cross-field checks or validate_<field>(self, value) for per-field checks.

Custom Filtering

A ModelFilter can override how an individual field translates to an ORM query by defining filter_<field>(self, qs, value). This is invoked instead of the default ORM lookup whenever a non-empty value is supplied for that field:

from django.db.models import Q

class TodoFilter(filters.ModelFilter):
    search = filters.CharField()  # not a model field — declared explicitly

    class Meta:
        model = Todo
        fields = ('completed', 'search')

    def filter_search(self, qs, value):
        return qs.filter(Q(title__icontains=value) | Q(detail__icontains=value))

?search=foo&completed=true runs Todo.objects.filter(completed=True).filter(Q(title__icontains='foo') | Q(detail__icontains='foo')). The completed field uses the default ORM lookup; search is dispatched to filter_search.

You can also override the entire filter(validated_data) method on a ModelFilter for full control — see the simple filter example above.

Foreign Keys

If your model contains foreign keys, then that field will become a choice field which can only have value according to that model object. If other value will be provided, validation error will be raised.

Filtering on Foreign Key data

If filtering on the basis of foreign key data is required, then source option can be used like this

class TodoFilter(filters.ModelFilter):
    user = filters.CharField(max_length=100, source='user.username')
    class Meta:
        model = Todo
        exclude = ('user',)

In above case, user__username={user} filter will be applied on queryset.