I'm using django-filter app. There is however one problem I do not know how to solve. It's almost exactly the same thing as is described in django documentation:

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

I want to make a query where I select all Blogs that has an entry with both "Lennon" in headline and was published in 2008, eg.:

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

Not to select Blogs that has an entry with "Lennon" in headline and another entry (possibly the same) that was published in 2008:

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

However, if I set up Filter such that there are two fields (nevermind __contains x __exact, just an example):

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 will generete the latter:

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

Is there a way to combine both filters into a single filter field?

有帮助吗?

解决方案

Well, I came with a solution. It is not possible to do using the regular django-filters, so I extended it a bit. Could've been improved, this is quick-n-dirty solution.

1st added a custom "grouped" field to django_filters.Filter and a filter_grouped method (almost copy of filter method)

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 {}

the only difference is that instead of creating a filter on query set, it returns a dictionary.

2nd updated BaseFilterSet qs method/property:

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

The trick is to store all "grouped" filters in a dictionary and then use them all as a single filter.

The filter will look something like this then:

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', ]
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top