改变django-filter的默认行为

时间:2013-09-22 18:41:47

标签: python django django-filter

这是一个django-filter应用专用猜测。

是否有人试图根据条件为过滤器引入条件?

让我举个例子:

假设我们有一个Product模型。它可以根据nameprice进行过滤。

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

我想更改此行为并添加ChoiceFilter,例如有两个选项:AND以及OR。从这一点来说,过滤器应该根据用户选择的内容工作。

EG。如果用户使用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.

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

@EDIT:

这是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 个答案:

答案 0 :(得分:2)

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

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

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

编辑1:

我写这篇文章是为了展示如何使用所选运算符查询两个字段。 https://gist.github.com/mariodev/6689472

用法:

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

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

它在重复使用方面实际上不是很灵活,但肯定可以重新考虑以使其有用。

答案 1 :(得分:2)

由于构造最终查询集的方式,使每个过滤器进行OR运算很困难。基本上,代码的工作原理如下:

FilterSet,filterset.py line 253

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

过滤器,filters.py line 253

def filter(self, qs):
    return qs.filter(name=self.value)

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

答案 2 :(得分:1)

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

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

我没有对此进行测试,但我认为你有了这个想法。 QuerySets支持按位操作,因此您可以轻松地将两个过滤器的结果与OR结合起来。

答案 3 :(得分: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