django-filter: declarative QuerySet filtering from URL parameters
A generic system for filtering Django QuerySets based on user selections
At a glance
- What is it?
- django-filter turns request parameters into filtered Django QuerySets through a FilterSet class that mirrors ModelForms. It is stable, BSD-licensed, and thin enough that its limits matter as much as its features.
- Who is it for?
- Adopt django-filter if you are building Django list views or DRF endpoints where filter fields map cleanly onto model fields, and you want that mapping declared in one class instead of hand-written request.GET parsing. Skip it if your filtering is computed from business rules that do not correspond to model fields, or if you only ever need one or two lookups that a plain queryset call already handles.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The request-parameter problem django-filter was written to remove
A Django list view that accepts filters usually starts as a few lines in the view function. Then the product owner asks for a price range, then for a manufacturer, then for a flag that means in stock. Each addition is another `if request.GET.get(...)` branch, another place where a missing parameter has to be distinguished from an empty one, and another chance to build a queryset that silently ignores what the user typed.
django-filter addresses that by moving the mapping from URL parameters to model lookups into a declarative class. The README describes it as a reusable Django application that lets users add dynamic QuerySet filtering from URL parameters, and the API is deliberately close to Django's ModelForms. A FilterSet declares a model and a list of fields; the view hands it request.GET and a queryset, and gets back a filtered queryset plus a form-like object you can render into a template.
The audience is Django developers maintaining search or browse pages, and developers building Django REST Framework endpoints that need query-parameter filtering. It is not aimed at people who need full-text search, ranking or faceted aggregation; those belong to a search engine, and django-filter does not pretend otherwise.
How a FilterSet turns request.GET into a filtered queryset
The mechanism is small. A FilterSet subclass declares `Meta.model` and `Meta.fields`. At instantiation it builds a form whose fields correspond to those declared fields, validates the incoming data, and only then applies the resulting lookups to the queryset. Passing `request.GET` with an unknown parameter does not raise; the parameter is simply not part of the declared set, so it never reaches the queryset.
That form layer is the part worth understanding, because it is where the library's behaviour comes from. Because the input is validated like a form, a value that cannot be coerced to the field's type is treated as invalid rather than passed through to the database. The README's own example builds a ProductFilter over a Product model with the fields name, price and manufacturer, then instantiates it in a view with `ProductFilter(request.GET, queryset=Product.objects.all())` and renders the result. The filtered queryset and the filter object travel together, which is why templates can render both the results and the controls that produced them.
For DRF the shape is the same but the import changes. The README directs you to `django_filters.rest_framework.FilterSet`, and the DRF integration is documented separately from the plain Django usage.
Installing django-filter with pip and filtering a first queryset
Installation is a pip install plus one entry in settings. The README gives both steps. The package name on PyPI is `django-filter`, while the import name is `django_filters`; the hyphen in one and the underscore in the other is a common source of confusion when copying examples.
pip install django-filterThen register the app so Django picks up its templates and form rendering support:
INSTALLED_APPS = [
...,
'django_filters',
]A first FilterSet follows the ModelForm pattern directly. Declare the model and the fields you want exposed as filter parameters. The README's Product example is the shortest complete version:
import django_filters
class ProductFilter(django_filters.FilterSet):
class Meta:
model = Product
fields = ['name', 'price', 'manufacturer']In the view, construct the filter with the request data and the base queryset, then render. The object you pass to the template is the filter itself, which carries the filtered queryset on its `qs` attribute:
def product_list(request):
filter = ProductFilter(request.GET, queryset=Product.objects.all())
return render(request, 'my_app/template.html', {'filter': filter})For a DRF viewset the only change the README shows is the import: use `from django_filters import rest_framework as filters` and subclass `filters.FilterSet` instead.
Where django-filter stops being the right tool
The library filters. It does not rank, score or aggregate. If a user expects results ordered by relevance to a free-text query, a FilterSet built over model fields will return exact or prefix matches in database order, and no amount of field declaration changes that. That is a job for a search backend, and the documentation does not claim otherwise.
The second limit is that the declarative model assumes your filters map onto model fields or simple lookups. Filters derived from computed state, from permissions, or from values that span several tables need custom filter methods, and at that point the declarative surface is doing less work than the code behind it. The README does not walk through that case; it points to the full documentation instead.
There is also a version floor that will surprise people upgrading an older project. The pyproject.toml declares `requires-python = ">=3.10"` and a dependency of `Django>=5.2`. A project pinned to an earlier Django cannot install a current release without also moving Django, and the README states plainly that support for Python and Django versions is dropped when they reach end-of-life, including Python versions still supported by a current Django. The README does not document a rollback path for that situation, so treat the upgrade as a decision rather than a patch.
django-filter against django-tables2 and hand-written queryset code
The nearest thing to a like-for-like alternative is django-tables2, and the difference is in what each one owns. django-tables2 owns presentation: it takes a queryset and renders a table, and while it can filter, filtering is a feature attached to the table. django-filter owns the input side and returns a queryset; rendering is left to your template. If your page is essentially a table with controls, the two are often used together rather than chosen between, with django-filter producing the queryset that django-tables2 displays.
The other alternative is no library at all. Hand-written `request.GET` parsing in the view is entirely workable for one or two parameters, and it avoids a dependency and a settings change. What it does not give you is the form layer: validation of incoming values, a bound form you can render, and a consistent answer to what happens when a parameter is absent. The trade is roughly a class per resource against a growing block of conditional code in each view. For a single filter, the class is overhead. For six, it is not.
Maintenance, versioning and the BSD licence
The repository is not archived, and the last push was on 2026-07-15. The README describes the project as mature and stable and sets out a two-part CalVer scheme where the first number is the year and the second is the release number within that year, so 23.1 means the first release of 2023. The release history bears out the stability claim: 21.1, 22.1 and 23.1 are each roughly a year apart, and the README says that other breaking changes are rare.
The upgrade contract is the part worth reading before you depend on it. Where a breaking change is required, the README states that every effort will be made to apply a year-plus-two deprecation period, giving the example of a change introduced in 23.x offering a fallback and finally being removed in 25.1. It also states the exception: where a fallback is not feasible, breaking changes arrive without deprecation and are called out in the release notes. That is a reasonable policy, but it means the CHANGES.rst file is the artefact you actually have to read on upgrade, not the version number.
Licensing is straightforward in practice and murky in metadata. The pyproject.toml declares `license = {text = "BSD"}`, the classifiers include the BSD License classifier, and a LICENSE file sits at the repository root. The repository's licence field is reported as NOASSERTION, which reflects how the metadata is detected rather than a dispute over terms. If your organisation requires an exact licence identifier for approval, read the LICENSE file directly instead of relying on a scanner's summary. This is not legal advice.
Editorial conclusion
Adopt django-filter if you are building Django list views or DRF endpoints where filter fields map cleanly onto model fields, and you want that mapping declared in one class instead of hand-written request.GET parsing. Skip it if your filtering is computed from business rules that do not correspond to model fields, or if you only ever need one or two lookups that a plain queryset call already handles. Before committing, check that your Django version satisfies the Django>=5.2 dependency in pyproject.toml and that your Python is 3.10 or newer, since those are hard floors rather than preferences.
Frequently asked questions
What is django-filter and what is it used for?
It is a reusable Django application that lets users add dynamic QuerySet filtering from URL parameters. You declare a FilterSet over a model and its fields, then hand it request.GET and a queryset in your view.
How do I install django-filter using pip?
Run pip install django-filter, then add 'django_filters' to INSTALLED_APPS. The package installs under the name django-filter but is imported as django_filters.
How do I use django-filter?
Instantiate the FilterSet with the request data and a base queryset, as in ProductFilter(request.GET, queryset=Product.objects.all()), then pass the resulting filter object to the template context. The README's example renders it as {'filter': filter}.
What is django-filter?
It is a reusable Django application for adding dynamic QuerySet filtering from URL parameters, with an API close to Django's ModelForms and a separate FilterSet for Django REST Framework.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/carltongibson-django-filter)