如何按可存字符/整数的grade字段排序Django Queryset(MS SQL后端)
解决方案:Django + MS SQL 下混合字符/数字 grade 字段的自定义排序
问题核心
需要对grade字段按首字母分组后排序,规则为:字母分组降序(如P、K)在前,数字分组升序(如1、2、3)在后,且因SQL Server版本限制无法使用__regex,同时要避免直接转换字段类型导致的报错。
实现思路
利用MS SQL内置函数做以下处理:
- 准确判断首字符是字母还是数字
- 给字母/数字分组设置排序优先级(字母优先)
- 分别为字母、数字设置对应的排序字段(字母按字符串降序,数字按整数升序)
代码实现
1. 自定义MS SQL函数封装
from django.db.models import Func, IntegerField, Case, When, F, Substr # 判断首字符是否为数字(基于MS SQL LIKE匹配) class IsDigit(Func): function = 'IIF' template = "%(function)s(%(expressions)s LIKE '[0-9]', 1, 0)" output_field = IntegerField() # 安全转换为整数(失败返回NULL,不报错) class TryCastInteger(Func): function = 'TRY_CAST' template = "%(function)s(%(expressions)s AS INT)" output_field = IntegerField()
2. 构建排序后的QuerySet
all_enrollments = ( Enrollment.objects.filter( location_id__in=location_ids, end_year=today.year, ) .order_by("-run_date") .annotate(first_letter_group=Substr("grade", 1, 1)) ) ordered_enrollment_list = ( all_enrollments .annotate( # 排序优先级:0=字母组(优先展示),1=数字组 sort_priority=Case( When(IsDigit('first_letter_group') == 1, then=1), default=0, output_field=IntegerField() ), # 字母组排序字段(降序) letter_sort=F('first_letter_group'), # 数字组排序字段(升序) number_sort=TryCastInteger('first_letter_group') ) # 最终排序逻辑:先优先级,再字母降序,最后数字升序 .order_by('sort_priority', '-letter_sort', 'number_sort') )
关键说明
IsDigit使用MS SQL的LIKE '[0-9]'精准判断数字字符,比ISNUMERIC更可靠(避免误判特殊符号)TryCastInteger替代普通Cast,转换失败时返回NULL而非抛出错误,保证查询稳定性- 通过
sort_priority实现字母组在前、数组在后的整体顺序,再分别对两组做目标排序
内容的提问来源于stack exchange,提问作者Hanny
相关产品推荐
相关产品推荐

