【问题标题】:Combine several filters into one filter() with django-filters使用 django-filters 将多个过滤器合并为一个 filter()
【发布时间】:2011-12-27 14:33:04
【问题描述】:

我正在使用django-filter 应用程序。但是有一个问题我不知道如何解决。这与 django 文档中描述的几乎完全相同:

https://docs.djangoproject.com/en/1.2/topics/db/queries/#spanning-multi-valued-relationships

我想进行查询,选择所有在标题中具有both“Lennon”的条目的所有博客并且于 2008 年发布,例如: p>

Blog.objects.filter(entry__headline__contains='Lennon', 
    entry__pub_date__year=2008)

不要选择标题中包含“Lennon”的条目和 2008 年发布的另一个条目(可能相同)的博客:

Blog.objects.filter(entry__headline__contains='Lennon').filter(
    entry__pub_date__year=2008)

但是,如果我将过滤器设置为有两个字段(没关系 __contains x __exact,只是一个示例):

class BlogFilter(django_filters.FilterSet):
    entry__headline = django_filters.CharFilter()
    entry__pub_date = django_filters.CharFilter()

    class Meta:
        model = Blog
        fields = ['entry__headline', 'entry__pub_date', ]

django-filter 将生成后者:

Blog.objects.filter(entry__headline__exact='Lennon').filter(
    entry__pub_date__exact=2008)

有没有办法将两个过滤器组合成一个过滤器字段?

【问题讨论】:

  • 很抱歉恢复了这个旧线程,但Foo.objects.filter(foo="bar").filter(qux="quux") 实际上与Foo.objects.filter(foo="bar", qux="quux") 相同,多个关键字参数和随后的过滤器是“AND”在一起的。要创建“或”过滤器,您必须使用 Q 对象:docs.djangoproject.com/en/dev/topics/db/queries/…
  • @cvk 嗨,根据docs.djangoproject.com/en/dev/topics/db/queries/…(开发版和 1.2 版),使用两个链式过滤器会生成“选择所有包含标题中带有“列侬”条目的博客以及一个条目发表于 2008 年”,但是当使用带有逗号的单个博客时,它会产生“选择所有包含标题中带有“列侬”的条目且于 2008 年发布的博客(同一条目同时满足两个条件)”。这是一个巨大的差异。我还没有找到使用 django-filter 的方法

标签: python django django-filter


【解决方案1】:

好吧,我想出了一个解决方案。使用常规的 django-filters 是不可能的,所以我对其进行了一些扩展。本来可以改进的,这是快速n-dirty的解决方案。

第一次在 django_filters.Filter 和一个 filter_grouped 方法(几乎是过滤器方法的副本)中添加了一个自定义的“分组”字段

class Filter(object):

    def __init__(self, name=None, label=None, widget=None, action=None,
        lookup_type='exact', required=False, grouped=False, **kwargs):
        (...)
        self.grouped = grouped

    def filter_grouped(self, qs, value):
        if isinstance(value, (list, tuple)):
            lookup = str(value[1])
            if not lookup:
                lookup = 'exact' # we fallback to exact if no choice for lookup is provided
            value = value[0]
        else:
            lookup = self.lookup_type
        if value:
            return {'%s__%s' % (self.name, lookup): value}
        return {}

唯一的区别是它不是在查询集上创建过滤器,而是返回一个字典。

第二次更新 BaseFilterSet qs 方法/属性:

class BaseFilterSet(object):
    (...)
    @property
    def qs(self):
        if not hasattr(self, '_qs'):
            qs = self.queryset.all()
            grouped_dict = {}
            for name, filter_ in self.filters.iteritems():
                try:
                    if self.is_bound:
                        data = self.form[name].data
                    else:
                        data = self.form.initial.get(name, self.form[name].field.initial)
                    val = self.form.fields[name].clean(data)
                    if filter_.grouped:
                        grouped_dict.update(filter_.filter_grouped(qs, val))
                    else:
                        qs = filter_.filter(qs, val)
                except forms.ValidationError:
                    pass

            if grouped_dict:
                qs = qs.filter(**grouped_dict)

        (...)
    return self._qs

诀窍是将所有“分组”过滤器存储在字典中,然后将它们全部用作单个过滤器。

过滤器将如下所示:

class BlogFilter(django_filters.FilterSet):
    entry__headline = django_filters.CharFilter(grouped=True)
    entry__pub_date = django_filters.CharFilter(grouped=True)

    class Meta:
        model = Blog
        fields = ['entry__headline', 'entry__pub_date', ]

【讨论】:

    猜你喜欢
    • 2016-03-26
    • 2017-03-28
    • 1970-01-01
    • 2022-09-23
    • 2021-11-02
    • 1970-01-01
    • 2018-06-16
    • 2016-02-21
    • 2015-02-23
    相关资源
    最近更新 更多