A Django REST Framework filter backend that lets you declaratively validate query parameters and filter querysets using DRF serializer syntax. Validate query params the same way you validate request bodies, with the same fields, the same Meta, and the same validate_* hooks.
📖 Full docs: https://dj-rest-filters.readthedocs.io/en/latest/
- DRF-native syntax — filters look like serializers; reuse what you already know.
- Validation-only mode — drop the queryset to use a filter purely for query-param validation; cleaned values land on
request.cleaned_args. ListFieldandRangeFieldforINand range queries; accepts comma-separated, JSON-array, or repeated-key inputs.- Custom per-field filtering via
filter_<name>(self, qs, value), pluslookup_expr,exclude=True, anddistinct=Trueon every field. - OpenAPI / Swagger — schema parameters auto-generated for both drf-spectacular and drf-yasg.
- Browsable API support out of the box.
| Minimum | Tested up to | |
|---|---|---|
| Python | 3.8 | 3.14 |
| Django | 3.2 | 5.2 |
| Django REST Framework | 3.12 | 3.17 |
pip install dj-rest-filtersAdd djfilters to your INSTALLED_APPS:
INSTALLED_APPS = [
...,
"djfilters",
]Define a filter — same shape as a DRF serializer:
from djfilters import filters
class TodoFilter(filters.Filter):
title = filters.CharField(required=False, lookup_expr="icontains")
completed = filters.BooleanField(required=False)
tags = filters.ListField(child=filters.CharField(), required=False)Wire it into a view:
from rest_framework import generics
from djfilters.backend import DjFilterBackend
class TodoView(generics.ListAPIView):
queryset = Todo.objects.all()
serializer_class = TodoSerializer
filter_class = TodoFilter
filter_backends = [DjFilterBackend]GET /todos/?title=buy&completed=true&tags=urgent,today filters by title__icontains="buy", completed=True, and tags__in=("urgent", "today").
Omit queryset to use a filter purely for query-param validation. The filter still runs, errors still surface as 400s, and the cleaned values land on request.cleaned_args:
class TodoView(generics.GenericAPIView):
serializer_class = TodoSerializer
filter_class = TodoFilter
filter_backends = [DjFilterBackend]
def get(self, request):
params = request.cleaned_args # validated query params
...Just like serializer context, extra context can be passed to the filter from the view:
class TodoView(generics.ListAPIView):
...
def get_filter_context(self):
return {"user": self.request.user}The returned dict is merged with the default {"request": ...} and is accessible as self.context inside the filter.
MIT — see LICENSE.