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
Допускаю, что может быть лучшее решение, но после трех дней копания я не нашел ничего хотя бы похожего на то, что мне нужно.