How to use DRF serializer fields as django-filter filter fields?
I’m working with Django REST Framework and django-filter to implement API filtering.
I created custom serializer fields (for example, a JalaliDateField that converts between Jalali and Gregorian dates, and applies Django’s timezone settings).
I expected that I could just pass these serializer fields into a django_filters.Filter by setting field_class, but it turns out Filter.field_class is only compatible with django.forms.Field, even when using django_filters.rest_framework.
So my question is: Is there a clean way to make django-filters work directly with DRF serializer fields?
What I tried
- Naively plugging in DRF serializer fields:
class JalaliDateFilter(django_filters.Filter):
field_class = MyCustomJalaliDateSerializerField
This fails, since django-filters expects a forms.Field, not a DRF serializers.Field.
- Proposed solution #1: Write a wrapper that adapts DRF fields into Django form fields Here’s a minimal sketch:
from django import forms
from rest_framework import serializers
class DRFFieldFormWrapper(forms.Field):
"""
Wrap a DRF serializer field so it can behave like a Django form field.
"""
def __init__(self, drf_field: serializers.Field, *args, **kwargs):
self.drf_field = drf_field
kwargs.setdefault("required", drf_field.required)
kwargs.setdefault("label", getattr(drf_field, "label", None))
super().__init__(*args, **kwargs)
def to_python(self, value):
if value in self.empty_values:
return None
return self.drf_field.run_validation(value)
def prepare_value(self, value):
return self.drf_field.to_representation(value)
Then, a custom filter:
import django_filters
class DRFFieldFilter(django_filters.Filter):
def __init__(self, *args, drf_field=None, **kwargs):
if drf_field is None:
raise ValueError("drf_field is required")
kwargs["field_class"] = lambda *a, **kw: DRFFieldFormWrapper(drf_field(), *a, **kw)
super().__init__(*args, **kwargs)
Usage:
class MyFilterSet(django_filters.FilterSet):
jalali_date = DRFFieldFilter(drf_field=MyCustomJalaliDateSerializerField)
- Proposed solution #2: Reimplement my DRF fields as Django
forms.Fieldclasses Basically duplicate my custom serializer field logic (Jalali date parsing, timezone handling) into form fields, and use those directly indjango_filters.Filter.
My question
- Is there an existing/best-practice way to make
django-filteruse DRF serializer fields directly? - Between the two options above (wrapping DRF fields vs. duplicating them as form fields), which is considered cleaner and more maintainable?
django-filter is built on top of Django’s forms.fields , not DRF’s serializers.Field, so you can’t plug serializer fields in directly. There isn’t a first-class “use serializer fields in filters” hook.
That leaves you with two options:
Wrap serializer fields in a forms.Field adapter (like your DRFFormFieldWrapper). This is a reasonable approach if you want to reuse the exact parsing/validation logic you already have in DRF fields. It keeps things DRY, but you’ll need to maintain the wrapper.
Implement the parsing at the forms layer (i.e. write a custom forms.Field for Jalali dates). This is the more idiomatic solution in the Django ecosystem, because filters conceptually belong to the forms layer, not the serializer layer.
If you care about maintainability and alignment with the rest of the Django stack, option #2 is the “best practice”. If avoiding duplication is more important and you’re comfortable with a thin adapter, option #1 is fine.
There’s no built-in way to bridge the two layers, so the choice depends on whether you want to stay idiomatic (forms-based) or DRY (wrapper-based).