Django ORM使用order_by时,如何将带符号字段归类排序?
解决Django ORM排序忽略符号的自定义排序需求
要实现无符号开头条目排最前、接着是@开头、最后是$开头的分组排序,核心思路是自定义排序优先级字段,先按优先级分组,再按字段本身排序。
方法一:Django ORM原生语法实现(跨数据库兼容)
通过annotate生成排序优先级字段,给不同开头类型的条目分配不同优先级数值,再按优先级+原字段排序:
from django.db.models import Case, When, Value, IntegerField # 生成排序优先级:普通字母开头→@开头→$开头→其他 sorted_items = Item.objects.annotate( sort_priority=Case( # 匹配字母开头的条目 When(name__regex=r'^[A-Za-z]', then=Value(1)), # 匹配@开头的条目 When(name__startswith='@', then=Value(2)), # 匹配$开头的条目 When(name__startswith='$', then=Value(3)), # 其他情况默认优先级 default=Value(4), output_field=IntegerField() ) ).order_by('sort_priority', 'name')
执行后即可得到目标结果:A,C,D,F,@B,@E,$G,$H。
方法二:原生SQL自定义排序(针对特定数据库)
如果项目固定使用某类数据库,直接用原生SQL生成排序字段性能更优:
PostgreSQL版本
sorted_items = Item.objects.extra( select={ 'sort_priority': """ CASE WHEN SUBSTRING(name FROM 1 FOR 1) ~ '^[A-Za-z]$' THEN 1 WHEN SUBSTRING(name FROM 1 FOR 1) = '@' THEN 2 WHEN SUBSTRING(name FROM 1 FOR 1) = '$' THEN 3 ELSE 4 END """ } ).order_by('sort_priority', 'name')
MySQL版本
sorted_items = Item.objects.extra( select={ 'sort_priority': """ CASE WHEN LEFT(name, 1) REGEXP '^[A-Za-z]$' THEN 1 WHEN LEFT(name, 1) = '@' THEN 2 WHEN LEFT(name, 1) = '$' THEN 3 ELSE 4 END """ } ).order_by('sort_priority', 'name')
补充说明
默认order_by忽略符号是因为数据库的**排序规则(collation)**默认会忽略非字母数字符号,通过自定义优先级字段,就能强制按需求分组排序。
内容的提问来源于stack exchange,提问作者Bigbob556677
相关产品推荐
相关产品推荐

