Skip to content

Matches ~ Quickstart Guide to django filters

Chloe Jiyeong Lee edited this page Nov 14, 2023 · 5 revisions

A guide on using django-filters with django-tables2. This is a guide on how to add a search bar and filters to a django project, using the library django-filter. For a full guide, find documentation here. It is worth noting that creating filters using django-filters looks much like creating a table using django-tables2.

You can install the library by running pip install django-filters. Note: If you are reading this after Fall 2023, django-filters is already in Chigame’s requirements.txt.

Creating Filter Classes

Create a file in your Django app directory (e.g., users, games) named filters.py. This file will contain filter classes, which correspond to particular models in your Django ORM. The following code block provides an example for the Lobby model in the games app:

import django_filters
from .models import Lobby

class LobbyFilter(django_filters.FilterSet):
   class Meta:
      model = Lobby
      fields = {
         ‘game’ : [‘icontains’],    # 'icontains' is for case-sensitive containment
         ‘max_players’ : [‘lt’,’gt’],    # less than and greater than
      }

Meta.fields is the parameter of interest. Here, ‘game’ is an attribute of the Lobby model, and the corresponding list are the lookup expressions which we want to have for that attribute. These lookup expressions are your filters; you may find a list of them here. Note that you can have multiple filters on one attribute of a model.

Note that Meta.fields is the shortest way to declare a filter, but methods with greater customizability can be found here.

The order of filters in the fields dictionary affects the order of filters in the generated form. Keep this in mind in case you want to arrange filters in a logical or meaningful order.

Adding Filters to your View

With a FilterSet for a particular model built, we pass those filters to an html page through its view, much like a table. Make sure to import your filters to views.py from filters.py, and the FilterView class from django-filters Continuing with the previous example:

from .filters import LobbyFilter
from django_filters.views import FilterView

…

class LobbyListView(FilterView, SingleTableView):
    model = Lobby
    table_class = LobbyTable
    template_name = "games/lobby_list.html"
    filterset_class = LobbyFilter  

    def get_table_data(self):
        self.filter = self.filterset_class(self.request.GET, queryset=super().get_table_data())
        return self.filter.qs

We have to override the default get_table_data method here, which is inherited from SingleTableView from django-tables2: this allows us to change the queryset which populates the table to our filtered data.

Function-based views are rather cleaner here. We simply pass the filter in the context of the render statement. For such a hypothetical lobby_list function, this would appear as follows:

def lobby_list(request):
    queryset = Lobby.objects.all()
    filter = LobbyFilter(request.GET, queryset=queryset)
    table = LobbyTable(filter.qs)

    return render(request,”games/lobby_list.html”, {'table': table, 'filter': filter})

Handling Multiple Filters

Filters are combined using AND logic by default, which is what results from LobbyFilter(request.Get, queryset=queryset). We can adjust this behavior if needed. For example, LobbyFilter(request.Get, queryset=queryset, conjoined=False) combines filters using OR logic.

Adding Filters to your HTML template

It remains to make the filters accessible from the actual webpage on which our django-tables2 table displays. We need to add a form here, which will be populated by our filters, as well as a submission button to have our chosen filters take their effect. We may also like to add a "clear filters" button to clear all filters, where "?" allows us to return to the same URL without the queryset. To continue with our example: the filters have been passed to our games/lobby_list.html page as context under the name filter. We add the following html <form> above our table (although, it doesn’t matter where this form is added in the html):

 <form method="get">
    {{ filter.form.as_p }}
    <button type="submit">Search</button>
    <a href="?">Clear Filters</a>
  </form>
  {% render_table table %}

In case we need to debug, django-filters automatically performs validation, and error messages can be accessed through {{ filter.form.errors }}

We now have advanced filtering capabilities on our lobbies table.

Clone this wiki locally