Как использовать поля сериализатора DRF в качестве полей фильтра django-filter?

Я работаю с фреймворком Django REST и django-filter для реализации фильтрации API.

Я создал пользовательские поля сериализатора (например, JalaliDateField, которые преобразуют даты по-джалалийски в григорианские и применяют настройки часового пояса Django).

Я ожидал, что смогу просто передать эти поля сериализатора в django_filters.Filter, установив field_class, но оказалось, что Filter.field_class совместим только с django.forms.Field, даже при использовании django_filters.rest_framework.

Итак, мой вопрос таков: Есть ли простой способ заставить django-фильтры работать напрямую с полями сериализатора DRF?

<время работы/>

То, что я пробовал

  1. Наивное подключение полей сериализатора DRF:
class JalaliDateFilter(django_filters.Filter):
    field_class = MyCustomJalaliDateSerializerField

Это не удается, поскольку django-filters ожидает forms.Field, а не DRF serializers.Field.

  1. Предлагаемое решение №1: Напишите оболочку, которая адаптирует поля DRF к полям формы Django Вот минимальный набросок:
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)

Затем пользовательский фильтр:

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)

Использование:

class MyFilterSet(django_filters.FilterSet):
    jalali_date = DRFFieldFilter(drf_field=MyCustomJalaliDateSerializerField)
  1. Предлагаемое решение №2: Переопределить мои поля DRF как классы Django forms.Field По сути, я дублирую мою пользовательскую логику полей сериализатора (анализ даты Jalali, обработка часовых поясов) в поля формы и использую их непосредственно в django_filters.Filter.
<время работы/>

Мой вопрос

  • Существует ли существующий / наилучший способ заставить django-filter напрямую использовать поля сериализатора DRF?
  • Один из двух вариантов, описанных выше (перенос полей DRF или дублирование их в виде полей формы), который считается более чистым и удобным в обслуживании?

django-filter построен поверх формуляров Django.fields, а не сериализаторов DRF.Field, поэтому вы не можете напрямую подключить поля сериализатора. Не существует первоклассного способа “использовать поля сериализатора в фильтрах”.

Таким образом, у вас остается два варианта:

  1. Перенесите поля сериализатора в форму.Адаптер полей (например, ваш DRFFormFieldWrapper). Это разумный подход, если вы хотите повторно использовать точную логику синтаксического анализа / проверки, которая уже есть в полях DRF. Это упрощает работу, но вам нужно будет поддерживать оболочку.

  2. Реализуйте синтаксический анализ на уровне форм (т.е. напишите пользовательские формы.Поле для дат Джалали). Это более идиоматичное решение в экосистеме Django, поскольку фильтры концептуально относятся к уровню форм, а не к уровню сериализатора.

Если вы заботитесь о ремонтопригодности и согласовании с остальной частью стека Django, вариант #2 является “наилучшей практикой”. Если для вас важнее избежать дублирования и вам удобен тонкий адаптер, то подойдет вариант #1.

Нет встроенного способа объединить эти два уровня, поэтому выбор зависит от того, хотите ли вы сохранить идиоматичность (на основе форм) или сухость (на основе оболочки).

Вернуться на верх