Lesson 1
Enhancing Your To-Do API with Filtering
Introduction

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.

Recap of Previous Setup: Part 1

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.

Python
1# 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
Python
1from rest_framework import serializers 2from .models import Todo 3 4class TodoSerializer(serializers.ModelSerializer): 5 class Meta: 6 model = Todo 7 fields = '__all__'
Recap of Previous Setup: Part 2

Next, we have a set of implemented CRUD operations with Django generics:

Python
1from 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.

Adding Filtering to the Todo API: Setup

To add filtering to our Todo API, we'll follow these steps:

  1. 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:

    Bash
    1pip install django-filter
  2. Update Project Settings: Now, let's update our Django settings to enable filtering:

    Python
    1# 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.

Adding Filtering to the Todo API: Updating Views

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 adding DjangoFilterBackend, we enable the use of django-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:

Python
1# 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.

Testing Single-Field Filters

Now let's test our single-field filters using send_request.py:

Python
1# 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 the params 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 set params={'completed': True}, the final URL will be http://0.0.0.0:3000/api/todos/?completed=True.
  • django-filters automatically handle returning only instances that satisfy the provided query parameters.
Using Multiple Filters

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:

Python
1# 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.

Testing Multiple-Field Filters

Let's extend the testing to include multiple-field filters:

Python
1# 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.

Summary and Next Steps

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!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.