philipn/django-rest-framework-filters

Feature: Supports OR operations between fields

mani098 opened this issue · 1 comments

Can we have an option to support OR operation to filter by value in multiple fields specified in URL
For example: /api/companies?search_fields=department__name,department__alias&search_value=Accounting
This will return queryset if Accounting present in either of the fields specified in the URL dynamically.

Reference:
from django.db.models import Q
qs = Company.objects.filter(Q(department__name=Accouting) | Q(department__alias=Accounting))

Hi @mani098, there are already a few options available.

If hardcoding the value for search_fields is acceptable, then you can use Filter.method. eg,

class MyFilterset(filters.FilterSet):
    search = filters.CharFilter(method='filter_search')

    def filter_search(self, qs, name, value):
        return qs.filter(Q(department__name=value) | Q(department__alias=value))

If you need to allow users to specify the fields, then you'll need to create a custom filter class. Something like the below should work. Keep in mind that I haven't actually ran this, so it's likely you'll need to fix it:

class SearchWidget(django_filters.widgets.SuffixedMultiWidget):
    suffixes = ['fields', 'value']


class SearchField(forms.MultiValueField):
    widget = SearchWidget

    def __init__(self, fields=None, *args, **kwargs):
        if fields is None:
            fields = (forms.CharField(), forms.CharField())
        super(SearchField, self).__init__(fields, *args, **kwargs)

    def compress(self, data_list):
        if data_list:
            return [data_list[0].split(','), data_list[1]]
        return None

class SearchFilter(filters.Filter):
    field_class = SearchField

    def filter(self, qs, value):
        fields, value = value

        return qs.filter(reduce(operator.or_,
            Q(**{name: value}) for name in fields
        ))