Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using django-filters, how can you do a lookup on multiple fields using OR?

Say I want to filter a built in django User model, but I only want to do so in 1 filter field, instead of a filter per field. That is, I want to emulate behaviour that django admin's search_fields (django admin search_fields docs), directly in the filter field.

Hence, for instance, instead of having a filter for field_name='first_name', then another filter for field_name'last_name' and so forth, I want to do something like field_name=['first_name', 'last_name', 'email', 'username'], where the same lookup_expr='icontains' can be used. Then the query is a simple OR lookup. Is this built in? I couldn't find it in the django-filter docs.

Or do I have to make custom filter for this. It seems like a very common use case.

like image 559
emihir0 Avatar asked Feb 05 '18 17:02

emihir0


2 Answers

I did it with a custom filter using Q objects.

import django_filters
from django.db.models import Q

class UserFilter(django_filters.FilterSet):
    multi_name_fields = django_filters.CharFilter(method='filter_by_all_name_fields')

    class Meta:
        model = User
        fields = []

    def filter_by_all_name_fields(self, queryset, name, value):
        return queryset.filter(
            Q(first_name__icontains=value) | Q(last_name__icontains=value) | Q(username__icontains=value) | Q(email__icontains=value)
        )

Then make sure to filter against the new filter field (multi_name_fields) and that should return what you are looking for.

like image 94
AXG1010 Avatar answered Oct 31 '22 11:10

AXG1010


I have done something similar. This should help:

def filter_by_all_name_fields(self, queryset, name, value):
"""
Split the filter value into separate search terms and construct a set of queries from this. The set of queries
includes an icontains lookup for the lookup fields for each of the search terms. The set of queries is then joined
with the OR operator.
"""
lookups = ['first_name__icontains', 'last_name__icontains', 'username__icontains', 'email__icontains',]

or_queries = [Q(**{lookup: value}) for lookup in lookups]

return queryset.filter(reduce(operator.or_, or_queries))
like image 3
saran3h Avatar answered Oct 31 '22 11:10

saran3h