SearchFilter 没有按自己预想的模糊搜索,自定义实现高级的django搜索

class SearchFilter(BaseFilterBackend):
    # The URL query parameter used for the search.
    search_param = api_settings.SEARCH_PARAM
    template = 'rest_framework/filters/search.html'
    lookup_prefixes = {
        '^': 'istartswith',
        '=': 'iexact',
        '@': 'search',
        '$': 'iregex',
    }
    search_title = _('Search')
    search_description = _('A search term.')

    def get_search_fields(self, view, request):
        """
        Search fields are obtained from the view, but the request is always
        passed to this method. Sub-classes can override this method to
        dynamically change the search fields based on request content.
        """
        return getattr(view, 'search_fields', None)

    def get_search_terms(self, request):
        """
        Search terms are set by a ?search=... query parameter,
        and may be comma and/or whitespace delimited.
        """
        params = request.query_params.get(self.search_param, '')
        params = params.replace('\x00', '')  # strip null characters
        params = params.replace(',', ' ')
        return params.split()
    
    # 删除掉无关函数

    def filter_queryset(self, request, queryset, view):
        search_fields = self.get_search_fields(view, request)
        search_terms = self.get_search_terms(request)

        if not search_fields or not search_terms:
            return queryset

        orm_lookups = [
            self.construct_search(str(search_field))
            for search_field in search_fields
        ]

        base = queryset
        conditions = []
        for search_term in search_terms:
            queries = [
                models.Q(**{orm_lookup: search_term})
                for orm_lookup in orm_lookups
            ]
            conditions.append(reduce(operator.or_, queries))
        queryset = queryset.filter(reduce(operator.and_, conditions))

        if self.must_call_distinct(queryset, search_fields):
            # Filtering against a many-to-many field requires us to
            # call queryset.distinct() in order to avoid duplicate items
            # in the resulting queryset.
            # We try to avoid this if possible, for performance reasons.
            queryset = distinct(queryset, base)
        return queryset

首先我们分析一下这个django自带的SearchFilter他的执行流程。

举个:

class Product(models.Model):
    name = models.CharField(max_length=100, verbose_name="产品名称")
    desc = models.CharField(max_length=256, default="", verbose_name="产品描述")
    created_at = models.DatetimeField()


class ProductSerializer(serializers.ModelSerializer):

    class Meta:
        fields = '__all__'


class ProductListView(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = (SearchFilter, )
    filter_class = ProductFilter
    search_fields = ['name', 'desc']

默认情况下,你不自定义参数的话,

SearchFilter会先去你在ProductListView下找到search_fields参数,找到你要模糊匹配的字段

www.xxx.com/product/?search=汽车,红色

然后再从链接里拿到你search的value 也就是汽车,红色,然后拆分成对应的两个terms ['汽车', '红色']

没有查询字段或者搜索词的话就不进行查询

然后根据你的search_fields的前缀是否有 ^ = @ $ 来使用对应的匹配方式,默认就是使用icontains,其实就是%terms%

然后关键的来了

for search_term in search_terms:
    queries = [
        models.Q(**{orm_lookup: search_term})
        for orm_lookup in orm_lookups
    ]
    conditions.append(reduce(operator.or_, queries))
queryset = queryset.filter(reduce(operator.and_, conditions))

他会先遍历你的查询词,再遍历每一个你要匹配的字段,然后用 或 的关系匹配每一个词查的字段,最后用 且 的关系匹配数据。

在例子里就是 (名称包含汽车, 或者描述包含汽车) 并且 (名称包含红色,或者描述包含红色)

也就是说我搜的这个能匹配到的就是 ①名称里有红色 有汽车 ②名称里有汽车 描述里有红色 ③名称里有红色 描述里有汽车 ④描述里有红色 有汽车

但是我们现在的需求是name必须包含红色 汽车 或者 desc包含红色 汽车,直接使用这个SearchFilter就不好使了,所以我们就自己重写这个匹配逻辑即可,当然你有其他的特殊逻辑也可以修改他。

搜索词全部在一个字段里的逻辑就是

for orm_lookup in orm_lookups:
    queries = [
        models.Q(**{orm_lookup: search_term})
        for search_term in search_terms
    ]
    conditions.append(reduce(operator.and_, queries))
queryset = queryset.filter(reduce(operator.or_, conditions))

简单来说就是继承SearchFilter,然后重写他的filter_queryset方法,改变底层逻辑即可。

先遍历字段,再遍历搜索词,第一个运算符用 and 且的关系 再用或 ,完美匹配了我们的需求。

你可能感兴趣的:(python,django,SearchFilter)