Reputation: 2938
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
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
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