My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more

How to filter querysets using django-filter package

Eugene Kwaka's photo
Eugene Kwaka
·Feb 17, 2022·

6 min read

While recently working on a personal Django project, I applied a search filter functionality by using the django-filter package. Django filter is a popular package that filters a queryset and displays the search results based on the model’s attributes.

In this article, I will guide you on how to apply the filter functionality in your project. To explain how the package works, I will create a basic Django web project with a model that will apply our filter. Follow up on each step carefully.

1. Setup Working Environment and Create Django Project

Firstly, you have to create a directory for your project and get inside the directory. The directory is where your project and your environment will be in.

$ mkdir myproject 
$ cd myproject

Next, create a virtual environment (venv) to store the project's dependencies using the py -m venv yourenvname command.

$ py -m venv yourenvname

You have to activate the virtual environment. You'll need to activate the venv each time you open the command prompt.

$ yourenvname\scripts\activate.bat

Inside the venv, you then install django.

$ pip install django

Let's create the project in our directory using django we've just installed.

$ django-admin startproject myproject

Move in to the project directory you have just created to run the server

$ cd myproject

Run the server using the command below and then visit this url http://127.0.0.1:8000/. You should see a Django Welcome Page to show you that the project was created successfully.

$ py manage.py runserver

2. Create an app for the project

Now let's create an app for the project. In the command line, stop the server by pressing Control+c. Then write the command below.

$ py manage.py startapp myapp

After creating the app, you have to add the app to the "INSTALLED_APPS" list in the myproject/settings.py file.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'myapp',
]

3. Create the Model

Let's create the model that we will use to implement the filter functionality. Name the model Order and create it in the file myapp/models.py.

from django.db import models

class Order(models.Model):
    STATUS = (
        ('Pending', 'Pending'),
        ('Out for delivery', 'Out for delivery'),
        ('Delivered', 'Delivered'),
    )
    CATEGORY = (
        ('Indoor', 'Indoor'),
        ('Outdoor', 'Outdoor'),
    )
    product = models.CharField(max_length=100, null=True)
    category = models.CharField(max_length=100, null=True, 
    choices=CATEGORY)
    status = models.CharField(max_length=100, null=True, 
    choices=STATUS)
    date_created = models.DateTimeField(auto_now_add=True, 
    null=True)

    def __str__(self):
        return self.product

Using the following commands, run the migrations to create a database based on the model above.

$ py manage.py makemigrations
$ py manage.py migrate

Register the model in the myapp/admin.py file.

from django.contrib import admin
from .models import Order

# Register your models here.

admin.site.register(Order)

3. Define the views.

Let's create a view function in the myapp/views.py file. We have to import the Order model from models.py file. The function will retrieve the order objects from the Order model.

from .models import Order

def index(request):
    orders = Order.objects.all().order_by('-date_created')
    context = {
            'orders': orders,
    }
    return render(request, 'index.html', context)

4. Create the url paths

Now we need to configure our url paths. Within the app folder, create a new urls.py file. myapp/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index)
]

Projects with multiple apps are common. The apps need their own dedicated url paths. We then update the main project's myproject/urls.py file to include the app's urls.

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('', include('myapp.urls')),
    path('admin/', admin.site.urls),
]

4. Create the Templates

In the root folder, let's create a directory for our project's HTML template file called index.html. The filter form will be in this template.

$ mkdir templates
$ touch templates/index.html

Next, we must configure the myproject/settings.py file to connect Django to our new template directory. Scroll through the file and change the settings in TEMPLATES.

import os
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')], #new
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Write the following code in the HTML file.

        <h5>ORDERS:</h5>
        <br>
        <div class="card card-body ml-3 mr-3">
            <table class="table table-sm">
                <tr>
                    <th>Product</th>        
                    <th>Category</th>
                    <th>Status</th>
                    <th>Date Ordered</th>
                </tr>
                {% for order in orders %}
                <tr>
                    <td>{{ order.product }}</td>
                    <td>{{ order.category }}</td>
                    <td>{{ order.status }}</td>
                    <td>{{ order.date_created}}</td>
                </tr>
                {% endfor %}
            </table>
        </div>

Let's create a superuser who will login to the admin page and add data that will be rendered in the template.

$ python manage.py createsuperuser

The html template will display data added by the superuser in the admin page.

Orders Table

5. Installing django-filter to Filter Querysets

Let's install the django-filter package by running the following command in your shell or command prompt.

$ pip install django-filter

Add the django-filter package in the INSTALLED_APPS list in the myproject/settings.py just like we added the project's app before.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'myapp',
    'django_filters',
]

Next, we create a filters.py file in the app directory. The filter will be based on the Order model. We will import the following filter arguments.

  • CharFilter matches the character values passed in the CharField or TextField

  • ChoiceFilter matches the values that are passed in the choices argument in the model.

  • DateTimeFromToRangeWidget filters values based on the range between two dates.

We are filtering the orders based on the following attributes:

  • product

  • category

  • status

  • date_created

import django_filters
from django_filters import DateTimeFromToRangeFilter, ChoiceFilter, CharFilter

from .models import Order

STATUS = (
    ('Pending', 'Pending'),
    ('Out for delivery', 'Out for delivery'),
    ('Delivered', 'Delivered'),
)

CATEGORY = (
    ('Indoor', 'Indoor'),
    ('Outdoor', 'Outdoor'),
)


class OrderFilter(django_filters.FilterSet):
    product = CharFilter(field_name = 'product', 
              lookup_expr='icontains')
    status = ChoiceFilter(choices=STATUS)
    category = ChoiceFilter(choices=CATEGORY)
    date_created = DateTimeFromToRangeFilter(
                  widget=django_filters.widgets.RangeWidget(
                  attrs={'type': 'date'}
    ))

    class Meta:
        model = Order
        fields = [
            'product',
            'category',
            'status',
            'date_created'
        ]

You will create a filter form by inheriting from django_filters.Filterset similar to creating Model Forms.

CharFilter will filter through the products using the lookup_expr='icontains' argument and display the results that match the characters entered in the form. ChoiceFilter will attribute to filtering by choices in the "category" and "status" fields, while DateTimeFromToRangeFilter will display the search results from filtering the orders by "date_created".

Define the logic for the OrderFilter form inside the myapp/views.py and then render it in the template. We then carryout the following steps in the view:

  1. Import the OrderFilter from filters.py.

  2. Add a GET request and the orders queryset we are filtering, to the variable myFilter.

  3. Rename the variable myFilter to orders queryset.

from .filters import OrderFilter

def index(request):
    orders = Order.objects.all().order_by('-date_created')
    myFilter = OrderFilter(request.GET, queryset=orders)
    orders = myFilter.qs
    context = {
        'orders': orders,
        'myFilter': myFilter,
    }
    return render(request, 'index.html', context)

Copy and paste the following code to create a form in index.html that will render the filter form. Note that the form method="GET" because you are sending a GET request for your search queries.

<div class="row">
    <div class="col">
        <div class="card card-body ml-3 mr-3">
            <form method="GET" action="" class="form-inline">
                {% csrf_token %}
                    <div>
                    {{ myFilter.form }}
                    <div>
                        <button type="submit" class="btn btn- 
                        secondary">SEARCH</button>
                    </div>
            </form>
        </div>
    </div>
</div>

Rerun the server and search your queries based on the filter form.

FilterForm Search for orders based on your filters of choice.

Search Results

The table will now display your search queries.

I hope you find this article helpful to your coding journey. Please share among your coder friends. In case of any questions, let's chat in the Comments Section. Happy coding.