es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo se filtra por un rango O un valor “null”? (Es decir, combinar el rango de filtro de número y el filtro booleano para null=True en un campo IntegerField)

Tengo un modelo Item con un campo numérico number. Este campo number tiene un valor predeterminado de null.

models.py

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

Quiero configurar filtros que puedan devolver un conjunto de consultas de Items donde el campo number esté en un rango, lo cual es bastante sencillo:

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)

Pero, ¿qué sucede si quiero tener un filtro adicional de tipo booleano que incluya Items que tengan null para el campo number, junto con cualquier Item que coincida con el rango de min_num y max_num?

Por ejemplo, una consulta de URL en forma de ?min_num=1&max_num=10&incl_null=True debería devolver todos los Items donde el campo number esté entre 1 y 10, O donde el campo number sea igual a None.

El siguiente código no funciona:

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")

  # no funciona
  def 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: He intentado los métodos en la documentación de “Filtrado por valores vacíos”, pero creo que eso es exclusivamente para valores null, donde estoy buscando una coincidencia de rango O un valor null.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para agregar un filtro booleano adicional que incluya Items con null para number, puedes modificar los métodos min_num_filter y max_num_filter en la clase ItemFilter de la siguiente manera:

    class ItemFilter(django_filters.FilterSet):
        min_num = django_filters.NumberFilter(method="min_num_filter")
        max_num = django_filters.NumberFilter(method="max_num_filter")
        incl_null = django_filters.BooleanFilter(method="incl_null_filter")
    
        class Meta:
            model = Item
            fields = ("min_num", "max_num", "incl_null")
    
        def min_num_filter(self, queryset, name, value):
            if self.cleaned_data["incl_null"]:
                return queryset.filter(Q(number__gte=value) | Q(number__isnull=True))
            return queryset.filter(number__gte=value)
    
        def max_num_filter(self, queryset, name, value):
            if self.cleaned_data["incl_null"]:
                return queryset.filter(Q(number__lte=value) | Q(number__isnull=True))
            return queryset.filter(number__lte=value)
    
        def incl_null_filter(self, queryset, name, value):
            return queryset
    

    Aquí, hemos agregado un objeto Q al filtro para number con un operador OR que incluye valores null si el valor booleano incl_null es True. Se accede al atributo cleaned_data para asegurarse de que el valor del filtro incl_null se considere en los filtros min_num y max_num. El método incl_null_filter simplemente devuelve el conjunto de resultados sin cambios.

    Con estas modificaciones, una consulta de URL en forma de ?min_num=1&max_num=10&incl_null=True debería retornar todos los Items donde number está entre 1 y 10 O number es igual a None.

Comments are closed.