2

这是一个django-filter应用程序特定的客户。

有没有人尝试引入条件让过滤器根据条件进行查询?

让我举个例子:

假设我们有一个Product模型。可以根据其name和进行过滤price

默认django-filter行为是,当我们使用更多过滤器并将它们链接在一起时,它们使用AND语句过滤数据(它缩小了搜索范围)。

我想改变这种行为并添加一个 ChoiceFilter,比如有两个选项:AND以及OR. 从这一点来看,过滤器应该根据用户的选择工作。

例如。如果用户查询带有 的产品name__startswith="Juice" OR price__lte=10.00,它应该列出名称以 开头的所有产品Juice以及价格低于 的产品10.00

Django-filter文档说过滤器可以接受一个参数:

action

An optional callable that tells the filter how to handle the queryset. It recieves a 
QuerySet and the value to filter on and should return a Queryset that is filtered 
appropriately.

这似乎是我正在寻找的,但文档缺乏任何进一步的解释。请问有什么建议吗?

@编辑:

这是views.py

def product_list(request):
    f = ProductFilter(request.GET, queryset=Product.objects.all())
    return render_to_response('my_app/template.html', {'filter': f})
4

4 回答 4

4

由于最终查询集的构造方式,很难将每个过滤器组合在一起。本质上,代码是这样工作的:

FilterSet,filterset.py 第 253 行

@财产
def qs(自我):
    qs = self.queryset.all()
    对于 self.filters() 中的 filter_:
        qs = filter_.filter(qs)

过滤器,filters.py 第 253 行

def过滤器(自我,qs):
    返回 qs.filter(name=self.value)

每个过滤器可以决定如何将自己应用于传入的查询集,并且所有过滤器,如当前实现的那样,使用 AND 过滤传入的查询集。您可以创建一组新的过滤器,将它们自己与传入的查询集进行 OR,但无法覆盖 FilterSet 端的行为。

于 2013-09-23T00:30:33.647 回答
2

为了使过滤器与 OR 一起使用,您应该创建一个 FilterSet 的子类并覆盖 Tim 的答案中的 qs,如下所示:

@property
def qs(self):
    qs = self.queryset.none()
    for filter_ in self.filters():
        qs |= filter_.filter(self.queryset.all())

我没有测试过这个,但我想你明白了。QuerySet 支持按位运算,因此您可以轻松地将两个过滤器的结果与 OR 结合起来。

于 2013-09-23T06:42:27.787 回答
2

action不会剪的。此回调用于特定的过滤器字段,并且只能访问该字段的值。

最干净的方法是创建多小部件过滤器字段,类似于RangeField. 查看源代码

因此,您可以使用两个日期字段nameprice并将逻辑类型[AND|OR]作为字段,这样您就可以一次访问所有这些值以在自定义查询集中使用。

编辑1:

这是我写的一个小要点,用来展示如何使用选定的运算符查询两个字段。 https://gist.github.com/mariodev/6689472

用法:

class ProductFilter(django_filters.FilterSet):
    nameprice = NamePriceFilter()

    class Meta:
        model = Product
        fields = ['nameprice']

它实际上在重用方面不是很灵活,但肯定可以对其进行重构以使其有用。

于 2013-09-22T20:17:01.790 回答
1



class FileFilterSet(django_filters.FilterSet):
    class Meta:
        model = File
        fields = ['project']

    def __init__(self, *args, **kwargs):
        super(FileFilterSet, self).__init__(*args, **kwargs)

        for name, field in self.filters.items():
            if isinstance(field, ModelChoiceFilter):
                field.extra['empty_label'] = None
                field.extra['initial'] = Project.objects.get(pk=2)
                # field.extra['queryset'] = Project.objects.filter(pk=2)


class FileFilter(FilterView):
    model = File
    template_name = 'files_list.html'
    filterset_class = FileFilterSet

于 2015-12-13T08:27:11.770 回答