DEV Community

Cover image for Comprehensive Guide to Filtering in Django REST Framework (DRF) with a Real-World Example
kihuni
kihuni

Posted on

Comprehensive Guide to Filtering in Django REST Framework (DRF) with a Real-World Example

When building APIs, you often deal with large datasets, and filtering is a must to allow users to retrieve only the data they need. Filtering in APIs allows users to search and narrow down results based on specific criteria. Thankfully, Django REST Framework (DRF) makes this process straightforward with the help of Django-filter, a powerful package that allows for flexible and efficient filtering.

In this blog post, we’ll walk through building an API with filtering using a real-world example. We’ll use Django-filter to handle common filtering operations, including searching by name, filtering by tech stack, and using date ranges. By the end, you’ll have a solid understanding of implementing filtering in your APIs.


Table of Contents:

  1. The Problem We're Solving
  2. Setting Up the Project
    • Project Initialization
    • Installing Django-filter
  3. Building the Models
  4. Creating the API Views
  5. Implementing Filtering with Django-filter
  6. Testing the API
  7. Conclusion

The Problem We're Solving

Imagine you’re building a project management application where users can create and manage projects. Users need to:

  • Search for projects by name.
  • Filter projects by tech stack (e.g., Python, JavaScript).
  • Retrieve projects created within a specific date range.

We’ll build an API to handle these requests efficiently using Django REST Framework and Django-filter.


Setting Up the Project

Project Initialization

First, let's set up a Django project. Open your terminal and run the following commands:

python shell commands
Now, add the project's app to INSTALLED_APPS in project_management/settings.py:

INSTALLED_APPS = [
    # Django default apps
    'rest_framework',
    'django_filters',  # Adding django-filter
    'projects',  # Our project app
]

Enter fullscreen mode Exit fullscreen mode

Configure DRF to use DjangoFilterBackend by adding this to settings.py:

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,  # Limit results per page to 10
}

Enter fullscreen mode Exit fullscreen mode

DjangoFilterBackend is the bridge between DRF and Django-filters, allowing you to use the powerful filtering capabilities of Django-filters in your DRF views.

Adding DjangoFilterBacked will allow all views that use the default filter backend to support filtering using the DjangoFilterBackend.

Building the Models

In the real world, project management systems often have models like Project and Category. Let’s create a Project model where each project has a name, description, tech stack, and creation date.

In projects/models.py, add the following code:

from django.db import models

class Project(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    tech_stack = models.CharField(max_length=255)  # e.g., Python, JavaScript
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

Enter fullscreen mode Exit fullscreen mode

We now have a basic model to store project information. Each project has a name, description, tech_stack (which lists technologies like Python, Django, etc.), and a created_at field to track when the project was added.

Next, we’ll create a migration and apply it:

python manage.py makemigrations
python manage.py migrate

Enter fullscreen mode Exit fullscreen mode

We now have the basic Project model set up in our database.
Let’s add some sample data into our Project model. You can create this data directly in your Django shell or by using the Django admin.

Step 1: Open Django Shell

python manage.py shell

Enter fullscreen mode Exit fullscreen mode

Step 2: Add Sample Data

from projects.models import Project
from datetime import datetime

# Sample data
Project.objects.create(name="Alpha Project", description="A project management tool.", tech_stack="Python, Django", created_at=datetime(2023, 1, 15))
Project.objects.create(name="Beta System", description="An inventory system.", tech_stack="Java, Spring Boot", created_at=datetime(2023, 2, 10))
Project.objects.create(name="Gamma Build", description="A continuous integration tool.", tech_stack="Python, Docker", created_at=datetime(2023, 3, 20))
Project.objects.create(name="Delta Tracker", description="A time tracking tool.", tech_stack="Node.js, Express", created_at=datetime(2023, 4, 25))

Enter fullscreen mode Exit fullscreen mode

Now we have four projects in our database, each with different tech stacks and creation dates. These will serve as our test data for the filtering functionality.

Creating the API Views

Let’s define an API that allows users to:

  • Retrieve a list of all projects.
  • Search for projects by name.
  • Filter projects by tech stack or creation date.

First, let's create a serializer in projects/serializers.py to convert Project instances into JSON:

from rest_framework import serializers
from .models import Project

class ProjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Project
        fields = ['id', 'name', 'description', 'tech_stack', 'created_at']

Enter fullscreen mode Exit fullscreen mode

Now, let’s define an API view to list all projects. Open projects/views.py and create a simple view using Django REST Framework’s ListAPIView:

from rest_framework import generics
from .models import Project
from .serializers import ProjectSerializer

class ProjectListView(generics.ListAPIView):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer

Enter fullscreen mode Exit fullscreen mode

Next, Create and add the route for this view in projects/urls.py:

from django.urls import path
from .views import ProjectListView

urlpatterns = [
    path('projects/', ProjectListView.as_view(), name='project-list'),
]
Enter fullscreen mode Exit fullscreen mode

Include this in your main urls.py file:

from django.urls import path, include

urlpatterns = [
    path('api/', include('projects.urls')),
]

Enter fullscreen mode Exit fullscreen mode

Now, you can run the server python manage.py runserver and visit /api/projects/ to see the list of all projects we created.

ListAPIView

But there’s no filtering yet, so let’s fix that next!

Implementing Filtering with Django-filter

To add filtering, we’ll use Django-filter’s FilterSet to allow users to search by project name, and tech stack, and filter by creation date.

Step 1: Define a FilterSet

Create a filter class in projects/filters.py:

import django_filters
from .models import Project


class ProjectFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
    tech_stack = django_filters.CharFilter(field_name='tech_stack', lookup_expr='icontains')


    class Meta:
        model = Project
        fields = ['name', 'tech_stack', 'created_at']


Enter fullscreen mode Exit fullscreen mode

This defines a custom filter class called ProjectFilter. The filter class extends django_filters.FilterSet, a base class provided by django-filter to define filters for a particular model.

In this class:

Two filters are defined: name and tech_stack.

name = django_filters.CharFilter(...): This defines a filter for the name field.

  • field_name='name': This tells the filter to target the name field in the Project model.

  • lookup_expr='icontains': This sets the lookup expression to icontains, this will filter for case-insensitive, partial matches on the name field. For example, searching for "alpha" will match project names like "Alpha Project", "alpha beta" or "ALPHA".

tech_stack = django_filters.CharFilter(...): This filter targets the tech_stack field and applies the same logic:

  • field_name='tech_stack': This tells the filter to target the tech_stack field in the Project model.

  • lookup_expr='icontains': This allows for case-insensitive, partial matching on the tech_stack field. Searching for "Python" will match any tech stack that includes "Python," " Django," or "React, Python."

The Meta class provides additional configuration for the ProjectFilter.

  • model = Project: This tells the filter that it is based on the Project model, meaning the filtering logic will be applied to the Project table in the database.

  • fields = ['name', 'tech_stack', 'created_at']: This defines the fields in the Project model that can be filtered. Learn more about django-filter here

Step 2: Add Filtering to the View

Now, update your ProjectListView to use filtering:

from django_filters.rest_framework import DjangoFilterBackend
from .filters import ProjectFilter

class ProjectListView(generics.ListAPIView):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer
    filter_backends = [DjangoFilterBackend]  # Enable filtering
    filterset_class = ProjectFilter  # Use our custom filter

Enter fullscreen mode Exit fullscreen mode

Testing the API

Let’s test the filtering functionality using some of the examples we created above.

Example 1: Searching by Project Name

To search for projects with "alpha" in their name, you can use this URL:

/api/projects/?name=alpha

Enter fullscreen mode Exit fullscreen mode

filter by name

Example 2: Filtering by Tech Stack

You can filter projects based on the technologies used in their tech stack. For example:

/api/projects/?tech_stack=Python

Enter fullscreen mode Exit fullscreen mode

filter by tech-stack

Conclusion
In this blog post, we’ve seen how to build an API with filtering in Django REST Framework using Django-filter. We covered how to:

  • Set up a project and create a basic API.
  • Implement search and filtering by name, and tech stack using FilterSet.
  • Test the filtering functionality with sample data.

By leveraging Django-filter you can offer efficient filtering options in your APIs with minimal effort. This helps reduce the load on your database, ensures faster queries, and provides a more tailored user experience.

Link for Further reading:

Django-filter official documentation
Djangorestframework

Link to example code: Filtering-in-Django-REST-Framework

Top comments (0)