In this lesson, we will enhance our To-Do API by adding filtering capabilities. Filtering is vital for any API as it allows users to retrieve specific subsets of data based on criteria like completion status or priority. Imagine you have a large list of tasks and you want to see only the completed ones or those with a specific priority. That's where filtering comes into play.
Let's dive into the process of incorporating filtering into our Django application, making our To-Do API more powerful and user-friendly.
Before we jump into adding filtering, let's briefly recap the essential components of our project setup. This will help you follow along better. First of all, we have a model and a serializer for it. This time, we will use a bit extended version of our Todo
model which includes the priority
field.
Python1# project/myapp/models.py 2from django.db import models 3 4class Todo(models.Model): 5 task = models.CharField(max_length=255) 6 completed = models.BooleanField(default=False) 7 priority = models.IntegerField() 8 9 def __str__(self): 10 return self.task
Python1from rest_framework import serializers 2from .models import Todo 3 4class TodoSerializer(serializers.ModelSerializer): 5 class Meta: 6 model = Todo 7 fields = '__all__'
Next, we have a set of implemented CRUD operations with Django generics:
Python1from rest_framework import generics 2from .models import Todo 3from .serializers import TodoSerializer 4 5class TodoListCreate(generics.ListCreateAPIView): 6 queryset = Todo.objects.all() 7 serializer_class = TodoSerializer 8 9class TodoDetail(generics.RetrieveAPIView): 10 queryset = Todo.objects.all() 11 serializer_class = TodoSerializer 12 13class TodoUpdate(generics.UpdateAPIView): 14 queryset = Todo.objects.all() 15 serializer_class = TodoSerializer 16 17class TodoDelete(generics.DestroyAPIView): 18 queryset = Todo.objects.all() 19 serializer_class = TodoSerializer
This code outlines the core components we built for our basic To-Do API. Next, we'll enhance it with filtering capabilities.
To add filtering to our Todo API, we'll follow these steps:
-
Install Django Filter: First, we need to install the
django-filter
library. If you're working on CodeSignal, this library is pre-installed, but for your local setup, you can install it using:Bash1pip install django-filter
-
Update Project Settings: Now, let's update our Django settings to enable filtering:
Python1# project/settings.py 2INSTALLED_APPS = [ 3 ... 4 'django_filters', 5] 6 7REST_FRAMEWORK = { 8 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'] 9}
This tells Django to use the
DjangoFilterBackend
for filtering. In most of the tasks, you will have this pre-defined.
Let's add filtering to our TodoListCreate
view. With django-filters
, it is easy and straightforward: you just need to add two new fields to your view: filter_backends
and filterset_fields
.
filter_backends
: This attribute in the view is used to specify one or more backends that the view should use for filtering. By addingDjangoFilterBackend
, we enable the use ofdjango-filters
.filterset_fields
: This attribute is a list of model fields that we allow users to filter by. For each field specified, users can filter the queryset using query parameters in the URL.
The updated view will look like this:
Python1# project/myapp/views.py 2from rest_framework import generics 3from .models import Todo 4from .serializers import TodoSerializer 5from django_filters.rest_framework import DjangoFilterBackend 6 7class TodoListCreate(generics.ListCreateAPIView): 8 queryset = Todo.objects.all() 9 serializer_class = TodoSerializer 10 filter_backends = [DjangoFilterBackend] 11 filterset_fields = ['completed']
Here, we import DjangoFilterBackend
and specify filterset_fields
to enable filtering by the completed
field.
Now let's test our single-field filters using send_request.py
:
Python1# send_request.py 2import requests 3 4base_url = 'http://0.0.0.0:3000/api/todos/' 5 6# Create a set of TODOs 7todos = [ 8 {"task": "Task 1", "completed": False, "priority": 1}, 9 {"task": "Task 2", "completed": True, "priority": 1}, 10 {"task": "Task 3", "completed": False, "priority": 2}, 11 {"task": "Task 4", "completed": True, "priority": 3} 12] 13 14# Create TODO items 15for todo in todos: 16 response = requests.post(base_url, data=todo) 17 print(f'Created TODO: {response.json()}') 18 19# Filter TODO items where completed is True 20response = requests.get(base_url, params={'completed': True}) 21print(f'TODOs where completed is True: {response.json()}') # Will print Task 2 and Task 4 22 23# Filter TODO items where completed is False 24response = requests.get(base_url, params={'completed': False}) 25print(f'TODOs where completed is False: {response.json()}') # Will print Task 1 and Task 3
- The
requests.get
method is used to make a GET request to an endpoint. We can pass query parameters using theparams
argument, which takes a dictionary. Each key of the dictionary is a model's field, and each value is the value that we want to filter with. - The query parameters are appended to the URL in the format
?key=value
. For example, if we setparams={'completed': True}
, the final URL will behttp://0.0.0.0:3000/api/todos/?completed=True
. django-filters
automatically handle returning only instances that satisfy the provided query parameters.
To filter tasks by multiple fields, such as those that are completed and have a priority of 2, you can update filterset_fields
to include more fields. Here is an example of an updated TodoListCreate
view:
Python1# project/myapp/views.py 2class TodoListCreate(generics.ListCreateAPIView): 3 queryset = Todo.objects.all() 4 serializer_class = TodoSerializer 5 filter_backends = [DjangoFilterBackend] 6 filterset_fields = ['completed', 'priority']
This code allows users to filter tasks by both completed
status and priority
.
Let's extend the testing to include multiple-field filters:
Python1# send_request.py 2# Filter TODO items where completed is True and priority is 1 3response = requests.get(base_url, params={'completed': True, 'priority': 1}) 4print(f'TODOs where completed is True and priority is 1: {response.json()}') # Will print Task 2
In this case, we include two fields into our params
dictionary to filter using both. The resulting request will be http://0.0.0.0:3000/api/todos/?completed=True&priority=2
. django-filters
will handle two parameters concatenated with &
in the query.
These examples show how the filtering works with your API endpoints.
In this lesson, we covered:
- How do you install and configure
Django Filter
? - Adding single-field and multi-field filters to your To-Do API.
- Testing the filters to ensure they work correctly.
Now, it's time for you to practice these concepts. In the upcoming exercises, you'll solidify your filtering understanding by applying these techniques in different scenarios. Experiment with various filter combinations to become more comfortable with this powerful feature.
By enhancing your To-Do API with filtering capabilities, you've taken a significant step towards making your application more efficient and user-friendly. Congratulations on reaching this milestone!