Django Rest Framework中查询前去除搜索词重音的实现方法
在Django 1.9中去除搜索词重音的实现方案
针对你的需求,要在执行查询前去除搜索词的重音,结合你的现有配置,咱们一步步来解决:
1. 先写个通用的去重音工具函数
首先需要一个能把带重音的字符转成普通字符的函数,用Python的unicodedata模块就能实现:
import unicodedata def remove_accents(input_str): # 把带重音的字符分解成「基础字符+重音标记」的组合 nfkd_form = unicodedata.normalize('NFKD', input_str) # 过滤掉重音标记,只保留基础字符 return "".join([c for c in nfkd_form if not unicodedata.combining(c)])
2. 自定义SearchFilter处理搜索词
Django REST Framework的默认SearchFilter不会处理重音,咱们需要重写它的get_search_terms方法,对用户输入的搜索词先做去重音处理:
from rest_framework.filters import SearchFilter class AccentInsensitiveSearchFilter(SearchFilter): def get_search_terms(self, request): # 先拿到默认的搜索词列表 terms = super().get_search_terms(request) # 给每个搜索词都做去重音处理 return [remove_accents(term) for term in terms]
然后更新你的视图配置,把原来的SearchFilter换成咱们自定义的过滤器:
# 替换原filter_backends中的SearchFilter filter_backends = (OrderingFilter, AccentInsensitiveSearchFilter)
3. 确保数据库能匹配无重音内容
只处理搜索词还不够,如果数据库里的name字段存的是带重音的内容,直接查询还是会匹配不到。这里给你两种可选方案:
方案A:保存时生成无重音字段(推荐,查询效率更高)
在模型里加一个不可编辑的字段,专门存无重音的名称,每次保存数据时自动生成:
class My_model(models.Model): class Meta: ordering = ['index'] name = models.CharField(max_length=250, verbose_name="名称") # 新增无重名字段,设置为不可编辑 name_unaccented = models.CharField(max_length=250, editable=False) def save(self, *args, **kwargs): # 保存前自动生成无重音版本的名称 self.name_unaccented = remove_accents(self.name) super().save(*args, **kwargs)
然后更新视图的search_fields,改成搜索这个无重音字段:
search_fields = ('name_unaccented', )
方案B:查询时用数据库函数处理字段(适合无法修改模型的场景)
如果用的是PostgreSQL数据库,它内置了unaccent函数,可以直接在查询时对字段做去重音处理。咱们需要再重写过滤器的filter_queryset方法:
from django.db.models import Func, Q from rest_framework.filters import SearchFilter # 定义调用PostgreSQL unaccent函数的表达式 class Unaccent(Func): function = 'UNACCENT' template = "%(function)s(%(expressions)s)" class AccentInsensitiveSearchFilter(SearchFilter): def get_search_terms(self, request): terms = super().get_search_terms(request) return [remove_accents(term) for term in terms] def filter_queryset(self, request, queryset, view): search_terms = self.get_search_terms(request) if not search_terms: return queryset search_fields = self.get_search_fields(view, request) if not search_fields: return queryset # 构造查询条件:同时匹配原字段和去重音后的字段 conditions = [] for term in search_terms: queries = [] for field in search_fields: # 对数据库字段应用Unaccent函数,再匹配去重音后的搜索词 queries.append(Q(**{f"{field}__icontains": term})) queries.append(Q(**{f"unaccent({field})__icontains": term})) conditions.append(Q(*queries)) queryset = queryset.filter(*conditions) # 处理结果去重(如果需要) if self.must_call_distinct(queryset, view): queryset = queryset.distinct() return queryset
如果是MySQL等其他数据库,需要对应调整数据库函数的实现,比如用convert函数结合合适的编码来去除重音。
验证效果
现在当用户输入带重音的搜索词(比如café),过滤器会先转成cafe,再去匹配数据库中对应的无重音内容,就能得到正确的查询结果了。
内容的提问来源于stack exchange,提问作者Dannick Montanede
相关产品推荐
相关产品推荐

