Search code examples
django-rest-frameworkdjango-filter

How to do 'or' when applying multiple values with the same lookup?


By referring to this article, I was able to implement the method of and search.

Django-filter with DRF - How to do 'and' when applying multiple values with the same lookup?

I want to know how to do or search for multiple keywords using the same field. How can I implement it?


Here is the code:

from rest_framework import viewsets
from django_filters import rest_framework as filters
from .serializers import BookInfoSerializer
from .models import BookInfo


class MultiValueCharFilter(filters.BaseCSVFilter, filters.CharFilter):
    def filter(self, qs, value):
        # value is either a list or an 'empty' value
        values = value or []

        for value in values:
            qs = super(MultiValueCharFilter, self).filter(qs, value)

        return qs


class BookInfoFilter(filters.FilterSet):
    title = MultiValueCharFilter(lookup_expr='contains')
    # title = MultiValueCharFilter(lookup_expr='contains', conjoined=False) -> get an error

    class Meta:
        model = BookInfo
        fields = ['title']


class BookInfoAPIView(viewsets.ReadOnlyModelViewSet):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    filter_class = BookInfoFilter

if I set conjoined=False like this title = MultiValueCharFilter(lookup_expr='contains', conjoined=False) get an error __init__() got an unexpected keyword argument 'conjoined'


Django 3.2.5

django-filter 2.4.0

djangorestframework 3.12.4

Python 3.8.5


Solution

  • You can try to modify the queryset returned from MultiValueCharFilter and combine values with operator.

    example:

    import operator
    from functools import reduce
    
    class MultiValueCharFilter(BaseCSVFilter, CharFilter):
    
        def filter(self, qs, value):
            expr = reduce(
                operator.or_,
                (Q(**{f'{self.field_name}__{self.lookup_expr}': v}) for v in value)
            )
            return qs.filter(expr)
    
    
    class BookInfoFilter(filters.FilterSet):
        title = MultiValueCharFilter(lookup_expr='contains')
    
        class Meta:
            model = BookInfo
            fields = ['title']