Rajnish Kumar
Rajnish Kumar

Reputation: 2938

How to use Django Filters to filter for many values for the same field?

Hi i am trying to do a filter using django filter and i can do filter like below

# function to do filter

class EmailFilter(django_filters.FilterSet):
   
   email_ml_recommendation = filters.CharFilter(method='filter_by_decision')
   email_broker = django_filters.CharFilter(field_name="email_broker", 
                     lookup_expr='icontains')


    class Meta:
       model = submission
       fields = '__all__'
    

def filter_by_decision(self, qs, name, value):

    if ',' in value:
        # filter for multiple selection
        if value.count(',') == 1:
            print('inside --')
            v_1 = value.split(',')[0]
            v_2 = value.split(',')[1]
            return qs.filter(
                Q(email_ml_recommendation__icontains=v_1) | Q(email_ml_recommendation__icontains=v_2)
            )
        elif value.count(',') == 2:
            v_1 = value.split(',')[0]
            v_2 = value.split(',')[1]
            v_3 = value.split(',')[2]

            return qs.filter(
                Q(email_ml_recommendation__icontains=v_1) | Q(email_ml_recommendation__icontains=v_2)|
                Q(email_ml_recommendation__icontains=v_3)
            )
        
    else:
        return qs.filter(
            Q(email_ml_recommendation__icontains=value)
        )

And calling my filter in below class

class SUBMISSIONFILTERVIEWSET(viewsets.ModelViewSet):
queryset = submission.objects.all().filter(email_today_mail=1).order_by('-email_last_updated')
serializer_class = SubmissionsSerializer
filter_backends = (DjangoFilterBackend, OrderingFilter, SearchFilter,)
filterset_class = EmailFilter
OrderingFilter()
# ordering_fields = ('broker_name')
ordering = ('-email_last_updated')  # default ordering can be overridden using ordering fields

the problme is i can filter on multiple values on the same field but for that i need to increase my Q parameter what i mean in order to do multi value filter for the same filed(email_ml_recommendation) i have to create a custom method filter_by_decision and inside that i have to use Q parameter same number of time as the value i get seperated by comman and problme is i do not know how many values can come form client is there a better way of doing this.

Upvotes: 2

Views: 1866

Answers (2)

Tribute12
Tribute12

Reputation: 17

I needed something like this but for strings and needed it to be more dynamic. I ended up using this:

import django_filters
import operator
from django.db.models import Q
from functools import reduce

class ListFilterField(django_filters.Filter):
    ''' This is a custom FilterField to enable a behavior like:
    ?log=taco,sandwich,burrito,ramen ...
    '''
    def filter(self, queryset, value):
        # If no value is passed, just return the initial queryset
        if not value:
            return queryset
        list_values = value.split(',') # Split the incoming query string by comma
        # Return a queryset filtered for every value in the list like 'taco OR burrito OR...'
        # Change operator.or_ to operator._and to apply all filters like 'taco AND burrito AND...'
        return queryset.filter(reduce(operator.or_, (Q(**{f'{self.field_name}__contains':x.strip()}) for x in list_values)))

Upvotes: 0

Yves Hary
Yves Hary

Reputation: 312

I want to share with you this solution I made for filtering a field with comma separated values:

class ListFilterField(Filter):
    """ This is a custom FilterField to enable a behavior like:
    ?id=1,2,3,4 ... 
    """

    def filter(self, queryset, value):

        # If no value is passed, just return the
        # initial queryset
        if not value: 
            return queryset

        self.lookup_expr = 'in' # Setting the lookupexpression for all values
        list_values = value.split(',') # Split the incoming querystring by comma

        if not all([item.isdigit() for item in list_values]): # In this case, I check on isdigit()
            raise serializers.ValidationError(
                'All values in {}s are not integer.'.format(str(list_values))
            )
        return super(ListFilterField, self).filter(queryset, list_values) # If everything is fine, I just pass the execution to the Filter-class

You can modify this solution and use this field like so:

class EmailFilter(django_filters.FilterSet):
   
   email_ml_recommendation = ListFilterField(field_name='your_field_name')
   email_broker = django_filters.CharFilter(field_name="email_broker", 
                     lookup_expr='icontains')


    class Meta:
       model = submission
       fields = '__all__'

Upvotes: 1

Related Questions