Django-filters Фильтр по булевым значениям или None с помощью IN lookup expr

Я пытаюсь создать фильтр для набора представлений DRF, который будет фильтровать поле моей модели:

    is_claim_submitted = models.BooleanField(
        IS_CLAIM_SUBMITTED_NAME, blank=True, null=True
    )

Я написал фильтр в соответствии с документацией по django-filter

class BoolInFilter(BaseInFilter, BooleanFilter):
    pass


class DefectFilter(django_filters.FilterSet):
    is_claim_submitted__in = BoolInFilter(
        field_name="is_claim_submitted",
        lookup_expr="in",
    )

    class Meta:
        model = Defect
        fields = {
            "id",
            "is_claim_submitted",
        }

Так вот, я пытаюсь отфильтровать набор запросов, отправляя запрос http://localhost:8000/api/v1/defects/is_claim_submitted__in=true,false. В основном, значения URI param должны быть в списке [True, False, None]. Но фильтрация не происходит, и значения не разбираются на python bools (true -> True). По какой-то причине он не получает field_class = forms.NullBooleanField из BooleanFilter, а использует парсинг по умолчанию Field.

Я уже проверил MRO, но так и не могу понять, почему мой код не работает. Проблема именно с BooleanFilter, так как все то же самое, но с NumberFilter и ids работает нормально.

Есть ли ошибка в моем коде или есть лучший способ реализовать такой фильтр?

После некоторого копания я наконец пришел к решению, хотя оно мне не нравится. Если кто-то придумает решение получше, пожалуйста, поделитесь им.

Я создал три класса, перезаписав начальные методы. Это мой класс фильтра. Он должен фильтровать

?is_claim_submitted=true,false,null

class DefectFilter(django_filters.FilterSet):
    is_claim_submitted = BooleanInFilter(
        field_name="is_claim_submitted",
        lookup_expr="in",
    )


    class Meta:
        model = Defect
        fields = {
            "is_claim_submitted",
        }

Далее мы идем по пути MRO. BooleanInFilter наследует от BooleanFilter, который просто определяет класс поля (field_class) под капотом.

class BooleanInFilter(BaseInFilter, BooleanFilter):
    field_class = MultipleNullBoleanField

    def filter(self, qs, value_list):
        ...filtering logic

Поэтому я изменяю его, чтобы переписать метод field_class' value_from_datadict. Этот метод извлекает данные из GET-параметров и сериализует их в python-значения. Однако оригинальный метод, несмотря на наследование от BaseInFilter (который должен разделять значения запятой), сначала пытается сериализовать значение и только потом разделить его. А мне нужно было по-другому: сначала разделить, а потом сериализовать каждое. Поэтому я реализовал следующий класс, который просто помещает функцию split в начало и добавляет цикл по разделенным значениям.

class MultipleNullBoleanField(forms.NullBooleanField):
    widget = MultipleNullBooleanSelect

class MultipleNullBooleanSelect(NullBooleanSelect):
    def value_from_datadict(self, data, files, name):
        values = data.get(name, "").split(",")

        parsing_dict = {
            True: True,
            "True": True,
            "true": True,
            False: False,
            "False": False,
            "false": False,
            "null": None,
        }

        values_list = []
        for value in values:
            parsed_value = parsing_dict.get(value, "")
            if parsed_value == "":
                continue
            values_list.append(parsed_value)

        if values_list is not None:
            if isinstance(values_list, list):
                return values_list
            return values_list.split(",")
        return None

Допускаю, что может быть лучшее решение, но после трех дней копания я не нашел ничего хотя бы похожего на то, что мне нужно.

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