如果我的数据中不存在查询值,为什么DjangoFilterBackend不返回任何结果?

时间:2018-11-29 00:13:30

标签: django django-rest-framework django-filter

我正在使用drf和django-filters对我的数据执行一些过滤。对于过滤后端,我使用django_filters.rest_framework.DjangoFilterBackend

class ProductsViewSet(LoginRequiredMixin, ModelViewSet):
    authentication_classes = (authentication.SessionAuthentication,)
    permission_classes = (permissions.IsAuthenticated,)

    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    filter_backends = (django_filters.rest_framework.DjangoFilterBackend,
                       rest_framework.filters.SearchFilter,
                       rest_framework.filters.OrderingFilter,)
    filter_class = ProductFilter

    search_fields = ("title", "description", "company", "country", "status",)
    ordering_fields = ("title", "description", "company", "country", "status",)

我的filter_class:

class ProductFilter(filters.FilterSet):

    price_from = filters.NumberFilter(field_name="price", lookup_expr="gte",
                                             label="Min price",
                                             min_value=0)
    price_to = filters.NumberFilter(field_name="price", lookup_expr="lte",
                                          label="Max price",
                                           min_value=0)

    # MultipleChoiceFilters
    country = filters.MultipleChoiceFilter(field_name="country", choices=COUNTRY_CHOICES)
    company = filters.MultipleChoiceFilter(field_name="company", choices=COMPANY_CHOICES)
    status = filters.MultipleChoiceFilter(field_name="status", choices=STATUS_CHOICES)

    class Meta:
        model = Product
        fields = ["price_from", "price_to", "country",
                  "company", "status"]
    @property
    def qs(self):
        parent = super(ProductFilter, self).qs
        return parent.order_by("-timestamp")

    def __init__(self, *args, **kwargs):
        super(ProductFilter, self).__init__(*args, **kwargs)
        user = self.request.user
        user_products = Product.objects.filter(user=user)

        # Initial Data #############################################################

        price_min = user_products.all().aggregate(Min("price"))["price__min"]
        price_max = user_products.all().aggregate(Max("price"))["price__max"]

        self.filters["price_from"].extra["initial"] = price_min
        self.filters["price_to"].extra["initial"] = price_max

        COUNTRY_CHOICES = tuple(Product.objects.filter(user=user).values_list("country", "country__name").distinct().order_by("country__name"))
        self.filters["country"].extra['choices'] = COUNTRY_CHOICES

        COMPANY_CHOICES = tuple(Product.objects.filter(user=user).values_list("company", "company").distinct().order_by("company"))
        self.filters["company"].extra['choices'] = COMPANY_CHOICES

我的问题是,如果我尝试使用多个值进行过滤,但其中一个不存在,则不会获得结果。

例如,如果我要基于外键(这是外键)进行筛选,请使用2个值:

  • id = 2的Google公司在我的任何对象中都不存在,

  • 公司ID为= 3的Microsoft公司确实存在,

以下内容将不返回任何结果

api/products?company=2&company=3

如果字段只是一个CharField,也会发生同样的情况。此外,使用filter_fields和filter_class时也会发生相同的行为。

起初,我实现了自己的过滤自定义get_queryset,但是我认为使用过滤器后端可以使事情变得不那么复杂。

我不确定为什么会发生这种情况,如果我要过滤的两个值都存在,那么一切正常。

2 个答案:

答案 0 :(得分:0)

  

我的问题是,如果我尝试使用多个值进行过滤,但其中不存在一个值,我将不会获得结果


这是因为过滤是以逻辑和方式进行的。也就是说,将创建与

类似的ORM查询

Product.objects.filter(company=1).filter(company=2)

在SQL中,类似于

SELECT ... from table WHERE company=1 and company=2


一种简单的解决方案是在过滤器类中添加 lookup_expr 自变量,

class ProductFilter(filters.FilterSet):
    # your code
    company = filters.MultipleChoiceFilter(
        field_name="company", choices=COMPANY_CHOICES,
        lookup_expr='in')

如果您在视图中使用 filter_class 自变量,则不会处理filter_fields自变量

答案 1 :(得分:0)

在您的ProductFilter类中,您可以指定字段用于运行查询的方法。使用过滤器,您需要将company字段的这些值组合在一起才能进行搜索。您正在做的是提供单独的值。您要做的就是将其更改为类似的内容;

api/products?company=2+3

class ProductFilter(django_filters.FilterSet):

    company = django_filters.CharFilter(
        label=_('Company'),
        method='company_filter'
    )

    def company_box_filter(self, qs, name, value):
        """
        Override the filter method in order to lookup for more than one field.
        """
        if not value:
            return qs

        ids = value.split()

        return qs.filter(id__in=ids)