Implementing AND / OR Filtering in Django REST Framework APIView

Other

In Django REST Framework (DRF), APIView does not support Django Filter Backend directly. Typically, Django Filter Backend works with GenericAPIView and above, but when using APIView, filtering must be implemented manually.

Especially when retrieving chart data or handling custom processing in an API, APIView is often preferred due to its flexibility in data processing and transformation. This article explains how to implement AND / OR filtering within APIView.

Additionally, when working with chart data or similar use cases, defining the same filtering logic in multiple views can be inefficient. To address this, we will introduce a shared filter class that can be inherited and reused across different views.

After learning this, you will be able to:

  • Apply AND conditions to specific parameters while applying OR conditions to others.
  • Use | to specify multiple OR values dynamically.
  • Modularize filter processing by creating a reusable filter class for multiple views.

By following this approach, you can ensure API flexibility, reduce code redundancy, and maintain a clean and scalable architecture.

Database Model (models.py)

Before diving into the implementation, let’s check the structure of the data we will use. Understanding what kind of data will be filtered makes the implementation more intuitive.

Below is an example of the database model used in this article:

from django.db import models

class testData(models.Model):
    type = models.CharField(max_length=255, blank=True, null=True)
    ItemName = models.CharField(max_length=255, blank=True, null=True)
    category = models.CharField(max_length=255, blank=True, null=True)
    ItemNum = models.IntegerField(blank=True, null=True)
    ItemColor = models.CharField(max_length=255, blank=True, null=True)
    baseDate = models.DateField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'testData'

    objects = models.Manager()

Sample Data

idtypeItemNamecategoryItemNumItemColorbaseDate
1αA111#13bda22025-01-01
2αB111#0c8c882025-01-01
3βB111#0c8c882025-01-01
4αC111#0163702025-01-01
5βC121#0163702025-01-01
6αD141#1730372025-01-01

Implementing AND / OR Filtering in APIView

Overview of AND / OR Conditions

When filtering data, the following conditions are applied:

  • type and category follow OR conditions (any matching value is valid)
  • ItemNum and ItemColor follow AND conditions (all specified conditions must match)
  • category can specify multiple values separated by | to apply an OR condition

Creating a Reusable Filter Class

from django.db.models import Q

class BaseFilter:
    OR_PARAMS = ['type', 'category']  # Parameters that follow OR conditions
    AND_PARAMS = ['ItemNum', 'ItemColor']  # Parameters that follow AND conditions

    def apply_filters(self, queryset, filters):
        query = Q()
        for param, value in filters.items():
            values = value.split('|')  # Split multiple values by '|'
            tmp_query = Q()
            for v in values:
                tmp_query |= Q(**{param: v})  # Create an OR condition for multiple values
            if param in self.AND_PARAMS:
                query &= tmp_query  # Apply AND condition for specified parameters
            else:
                query |= tmp_query  # Apply OR condition for other parameters
        return queryset.filter(query)

Implementing APIView

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import testData
from .filters import BaseFilter

class FilterAPIView(APIView, BaseFilter):
    def get(self, request):
        queryset = testData.objects.all()  # Retrieve all records from the database
        filtered_queryset = self.apply_filters(queryset, request.GET)  # Apply filters to the queryset
        return Response(filtered_queryset.values())  # Return the filtered data as a response

Example Request

GET /api/filter/?type=α&category=1|2&ItemNum=1&ItemColor=#0c8c88

Applied Filtering Conditions:

  • type is ‘α’ OR category is ‘1’ OR ‘2’
  • AND ItemNum is ‘1’ AND ItemColor is ‘#0c8c88’

SQL Representation:

SELECT * FROM testData 
WHERE (ItemNum = 1 AND ItemColor = '#0c8c88') 
AND (type = 'α' OR category = '1' OR category = '2');

This article introduced how to dynamically apply AND / OR conditions when using APIView. By utilizing the Q object and allowing multiple OR conditions with |, we achieved flexible filtering.

Additionally, we implemented a reusable common filter through class inheritance, allowing us to standardize the filter logic. This approach makes it easy to apply filtering to different APIs while improving maintainability.

By leveraging this method, you can build more flexible and extensible APIs! 🚀