如何按范围或 "null" 值过滤? (即结合 NumberFilter 范围和 BooleanFilter 为 null=True IntegerField)

How to filter by range OR "null" value? (I.e. combine NumberFilter range and BooleanFilter for null=True IntegerField)

我有一个带有数字 number 字段的 Item 模型。此 number 字段默认为空。

# models.py
class Item(models.Model):
  number = models.IntegerField(default=None, blank=True, null=True)

我想设置可以 return 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_nummax_num 范围?

因此,例如,?min_num=1&max_num=10&incl_null=True 形式的 URL 查询应该 return 所有 Items 其中 number1 之间10 OR 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

编辑: 我已经尝试了“Filtering by empty values”文档中的方法,但我认为那是专门针对 null 值的 - 我在这里寻找范围匹配 null 值。

嗯,我能想到的唯一解决方案是将最小范围、最大范围和 is_null 布尔值传递到单个 char 字段中,然后将其转换为 3 个单独的过滤器以进行操作.

所以查询 URL 看起来像 ?master_num=1-10-1 范围 1 - 10 incl。 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))