Django Rest Framework中按用户名和显示名搜索用户的问题
解决Django Rest Framework中用户搜索的子串匹配问题
你当前用istartswith实现的是前缀匹配,只能匹配字段开头的子串,所以会出现这些问题:
- 显示名
William Welch里的Welch不是字段开头,搜索时无法命中 - 用户名
123William里的23w、elch这类中间子串也匹配不到
下面给出两种可行解决方案:
方案1:改用icontains实现全子串匹配
直接把istartswith替换成icontains,就能匹配字段中任意位置的子串,且忽略大小写:
search_obj = User.objects.exclude(user_uuid=token_user_uuid).filter( Q(display_name__icontains=search_name) | Q(username__icontains=search_name) ).order_by('-created_on')[:10]
效果验证
针对你的测试案例,该方案能全部命中:
search_name=12→ 匹配123William的前缀部分search_name=23w→ 匹配123William的中间子串search_name=Wil→ 匹配William Welch和123Williamsearch_name=Welc→ 匹配William Welch中的Welch前缀search_name=elch→ 匹配William Welch中的Welch中间子串
性能提示
如果用户表数据量较大,icontains可能拉低查询速度。可以给username和display_name字段加数据库索引优化:
- PostgreSQL:创建
gin或gist类型索引 - MySQL:使用
fulltext索引(配合search查询更高效)
方案2:按词语前缀匹配(精准场景)
如果希望仅匹配显示名中独立词语的前缀(比如Welch是独立词能命中,但不会误匹配WilliamWelch这种连写的中间子串),可以用iregex实现正则匹配:
import re # 转义搜索关键词里的特殊字符,避免正则报错 escaped_search = re.escape(search_name) # 正则规则:匹配任意独立单词以搜索词开头(\b是单词边界) regex_pattern = rf'\b{escaped_search}' search_obj = User.objects.exclude(user_uuid=token_user_uuid).filter( Q(display_name__iregex=regex_pattern) | Q(username__icontains=search_name) # 用户名可按需改为regex规则 ).order_by('-created_on')[:10]
适用场景
适合用户显示名是全名的场景,搜索姓氏时能精准匹配,不会误匹配到名字中间的相似子串。
内容的提问来源于stack exchange,提问作者Zain Jack
相关产品推荐
相关产品推荐

