Как фильтровать по диапазону ИЛИ по значению "null"? (Т.е. объединить NumberFilter диапазона и BooleanFilter для null=True IntegerField)
У меня есть Item
модель с числовым number
полем. Это поле number
по умолчанию имеет значение null.
# models.py
class Item(models.Model):
number = models.IntegerField(default=None, blank=True, null=True)
Я хочу настроить фильтры, которые могут возвращать набор запросов Items
, где number
находится в диапазоне - что достаточно просто:
# filters.py
class ItemFilter(django_filters.FilterSet):
min_num = django_filters.NumberFilter(method="min_num_filter")
max_num = django_filters.NumberFilter(method="max_num_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
def min_num_filter(self, queryset, name, value):
return queryset.filter(number__gte=value)
def max_num_filter(self, queryset, name, value):
return queryset.filter(number__lte=value)
Но что если я хочу иметь дополнительный булевский фильтр, который может включать Items
, который имеет null
для number
вместе с тем, что Items
соответствует диапазону min_num
и max_num
?
Например, запрос URL в форме ?min_num=1&max_num=10&incl_null=True
должен вернуть все Items
, где number
находится между 1
и 10
ИЛИ number
равен None
.
Следующий код не работает:
class ItemFilter(django_filters.FilterSet):
...
incl_null = django_filters.BooleanFilter(method="incl_null_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
// doesn't work
class incl_null_filter(self, queryset, name, value):
if value is True:
return queryset | Item.objects.filter(number=None)
if value is False:
return queryset
Edit: Я пробовал методы в документации "Filtering by empty values", но я думаю, что это исключительно для значений null
- где я ищу совпадение диапазона OR a null
value.
Ну, единственное решение, которое я могу придумать, это передать min range, max range и is_null boolean в одно поле char
, а затем преобразовать его в 3 отдельных фильтра для выполнения действий.
Таким образом, URL запроса будет выглядеть как ?master_num=1-10-1
для диапазона 1 - 10
вкл. None
и ?master_num=1-10-0
для диапазона 1 - 10
вкл. None
.
class ItemFilter(django_filters.FilterSet):
master_num = django_filters.CharFilter(method="master_num_filter")
class Meta:
model = Item
fields = ("master_num")
def master_num_filter(self, queryset, name, value):
# array = [min, max, 1 or 0 for True and False]
array = value.split("-")
min = Q(year_published__gte=int(array[0]))
max = Q(year_published__lte=int(array[1]))
if array[2] == "1":
incl_null = Q(year_published=None)
return queryset.filter((min & max) | incl_null)
else:
return queryset.filter(min & max)
Хотелось бы узнать, есть ли лучший способ сделать это.
Попробуйте этот запрос:
from django.db.models import Q
min_ = 0
max_ = 10
Item.objects.filter(Q(number__gte=min_, number__lte=max_) | Q(number__isnull=True))