首页 > 解决方案 > 如何使用 Django Filters 过滤同一字段的多个值?

问题描述

嗨,我正在尝试使用 django 过滤器进行过滤,我可以进行如下过滤

# 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)
        )

并在下面的课程中调用我的过滤器

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

问题是我可以过滤同一字段上的多个值但为此我需要增加我的 Q 参数我的意思是为了对同一字段进行多值过滤(email_ml_recommendation)我必须创建一个自定义方法 filter_by_decision 和里面我必须使用 Q 参数与我用 comman 分隔的值相同的次数,问题是我不知道有多少值可以来自客户端,有没有更好的方法来做到这一点。

标签: pythondjangoapidjango-rest-framework

解决方案


我想与您分享我为过滤带有逗号分隔值的字段而制作的解决方案:

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

您可以修改此解决方案并使用此字段,如下所示:

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__'

推荐阅读