How to filter querysets using django-filter package
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.
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:
Import the OrderFilter from filters.py.
Add a GET request and the orders queryset we are filtering, to the variable myFilter.
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.
Search for orders based on your filters of choice.
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.