You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何按可存字符/整数的grade字段排序Django Queryset(MS SQL后端)

解决方案:Django + MS SQL 下混合字符/数字 grade 字段的自定义排序

问题核心

需要对grade字段按首字母分组后排序,规则为:字母分组降序(如P、K)在前,数字分组升序(如1、2、3)在后,且因SQL Server版本限制无法使用__regex,同时要避免直接转换字段类型导致的报错。

实现思路

利用MS SQL内置函数做以下处理:

  1. 准确判断首字符是字母还是数字
  2. 给字母/数字分组设置排序优先级(字母优先)
  3. 分别为字母、数字设置对应的排序字段(字母按字符串降序,数字按整数升序)

代码实现

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 05:45:34