You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django自定义icontainsall lookup:rhs仅接收%s问题求助

问题

想要创建一个自定义的icontainsall lookup,实现用户搜索带空格的短语时,将其转换为多个LIKE条件的AND查询(不限制单词顺序)。例如用户搜索'egg milk'时,生成的查询条件为name LIKE %egg% AND name LIKE %milk%。

现有lookup代码如下:

from django.db.models import CharField, Lookup, TextField


class CaseInsensitiveContainsAllLookup(Lookup):
    lookup_name = 'icontainsall'

    def as_sql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        print(lhs, rhs, lhs_params, rhs_params)
        # 输出为 "mytable"."name" %s [] ['egg milk']
        predicate, params = [], []
        # 转义\和%
        rhs_list = rhs.replace('\\', '\\\\').replace('%', '\\%').split(' ')
        for rhs in rhs_list:
            params += lhs_params + rhs_params
            predicate.append(f"LOWER({lhs}) LIKE LOWER({rhs})")
        return ' AND '.join(predicate), params


CharField.register_lookup(CaseInsensitiveContainsAllLookup)
TextField.register_lookup(CaseInsensitiveContainsAllLookup)

目前rhs仅接收%s而非实际的egg milk值,请问如何通过lookup实现预期需求?


解决方案

问题核心是你错误操作了占位符rhs,真实的搜索值存在rhs_params或self.rhs.value中。需要取出真实字符串拆分关键词,再逐个生成LIKE条件,同时处理通配符转义。

修改后的代码如下:

from django.db.models import CharField, Lookup, TextField


class CaseInsensitiveContainsAllLookup(Lookup):
    lookup_name = 'icontainsall'

    def as_sql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        # 取出真实的搜索字符串
        search_value = self.rhs.value
        predicate, params = [], []
        
        # 拆分关键词并过滤空字符串(避免连续空格生成无效条件)
        keywords = [kw.strip() for kw in search_value.split(' ') if kw.strip()]
        
        for keyword in keywords:
            # 转义SQL通配符:%、_、反斜杠
            escaped_keyword = keyword.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
            # 拼接模糊匹配模板
            like_pattern = f'%{escaped_keyword}%'
            # 添加条件和对应参数
            predicate.append(f"LOWER({lhs}) LIKE LOWER(%s)")
            params.extend(lhs_params)
            params.append(like_pattern)
        
        return ' AND '.join(predicate), params


CharField.register_lookup(CaseInsensitiveContainsAllLookup)
TextField.register_lookup(CaseInsensitiveContainsAllLookup)

关键修改说明

  1. 获取真实值:直接通过self.rhs.value拿到用户输入的原始搜索字符串,而非操作占位符%s
  2. 清理关键词:拆分后过滤空字符串,避免连续空格生成LIKE %%这类无效条件
  3. 通配符转义:对关键词中的%、_、\进行转义,防止用户输入的特殊字符干扰SQL逻辑
  4. 生成独立条件:每个关键词对应一个LIKE %keyword%,用AND连接,确保所有关键词都被匹配
  5. 参数绑定:正确收集每个条件的参数,保证参数顺序与SQL占位符一一对应

当用户搜索'egg milk'时,最终生成的SQL逻辑为:

WHERE LOWER("mytable"."name") LIKE LOWER(%s) AND LOWER("mytable"."name") LIKE LOWER(%s)

对应的参数是['%egg%', '%milk%'],完全符合预期的多关键词模糊匹配需求。


内容的提问来源于stack exchange,提问作者fishfin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 10:57:03