Django+DRF使用PostgreSQL非确定性排序规则搜索报错求解决方案
问题解决方案:DRF SearchFilter 搭配 PostgreSQL 非确定性排序规则字段报错
报错原因
PostgreSQL 禁止对非确定性排序规则的字段使用 LIKE 操作,而 DRF 的 SearchFilter 默认会对 search_fields 中的字段生成 icontains/contains 类型的查询,这类查询会转换为 LIKE 语句,因此触发 nondeterministic collations are not supported for LIKE 错误。
可行解决方案
方案一:自定义 SearchFilter 替换默认查找逻辑
重写过滤器,对目标字段改用 lower() 函数进行大小写不敏感匹配,避开 LIKE 依赖:
from django.db.models import Func, Value from rest_framework.filters import SearchFilter class CaseInsensitiveSearchFilter(SearchFilter): def filter_queryset(self, request, queryset, view): search_term = request.query_params.get(self.search_param, '') if not search_term: return queryset # 单独处理custom_field,其他字段保留默认逻辑 if 'custom_field' in view.search_fields: # 统一转换为小写后匹配 queryset = queryset.annotate( lower_custom=Func('custom_field', function='LOWER') ).filter(lower_custom=Func(Value(search_term), function='LOWER')) # 临时移除custom_field避免重复处理,之后恢复 remaining_fields = [f for f in view.search_fields if f != 'custom_field'] if remaining_fields: view.search_fields = remaining_fields queryset = super().filter_queryset(request, queryset, view) view.search_fields.append('custom_field') return queryset return super().filter_queryset(request, queryset, view)
在视图中使用自定义过滤器:
from rest_framework.viewsets import ModelViewSet class YourModelViewSet(ModelViewSet): filter_backends = [CaseInsensitiveSearchFilter] search_fields = ['custom_field', 'other_field'] # 其他视图配置...
方案二:将排序规则改为确定性模式
如果业务场景可以接受相对简单的 Unicode 大小写处理,可修改排序规则为确定性的,这样就能兼容 LIKE 操作:
调整排序规则定义:
[ CreateCollation( "case_insensitive", provider="icu", locale="und-u-ks-level2", # 移除-kn-true,启用确定性模式 deterministic=True, ), ]
注意:确定性大小写不敏感排序对部分特殊 Unicode 字符的处理可能不如非确定性规则全面,需根据业务需求测试验证。
方案三:新增小写冗余字段并建立索引
在模型中添加一个存储小写值的冗余字段,通过信号自动同步原字段内容,然后将该冗余字段加入 search_fields:
模型修改:
from django.db.models.signals import pre_save from django.dispatch import receiver class YourModel(models.Model): custom_field = models.CharField(db_collation="case_insensitive", db_index=True, max_length=100, null=True) custom_field_lower = models.CharField(db_index=True, max_length=100, null=True) # 其他模型字段... @receiver(pre_save, sender=YourModel) def sync_lower_field(sender, instance, **kwargs): if instance.custom_field is not None: instance.custom_field_lower = instance.custom_field.lower() else: instance.custom_field_lower = None
视图修改:
class YourModelViewSet(ModelViewSet): filter_backends = [SearchFilter] search_fields = ['custom_field_lower', 'other_field'] # 其他视图配置...
此方法性能最优,因为直接匹配索引字段,避免了函数调用的额外开销。
关于PostgreSQL规则的说明
PostgreSQL 推荐用 db_collation 替代旧的 CI 字段,但非确定性排序规则是为处理复杂 Unicode 大小写映射(如德语 ß 对应 SS)设计的,这类映射不具备确定性,因此无法支持依赖固定匹配逻辑的 LIKE 操作;而确定性排序规则的大小写不敏感基于固定字符映射,所以可以兼容 LIKE。
内容的提问来源于stack exchange,提问作者user2880391
相关产品推荐
相关产品推荐

