Django如何按CharField类型case_number字段的整数部分对queryset排序
实现方案
你之前的写法无法生效是因为Cast会尝试将整个case_number字段值转换为整数,遇到非数字字符就会转换失败,所以需要先提取字段开头的数字前缀再做转换排序,以下是几种可行方案:
方案1:数据库层面注解实现(无需修改模型,兼容QuerySet链式调用)
根据你使用的数据库类型选择对应的注解写法,以下是主流数据库的实现示例:
PostgreSQL
from django.db.models import F, Func, Cast, IntegerField qs = MyObject.objects.annotate( # 提取开头的数字前缀 num_prefix=Func( F('case_number'), function='substring', template=r"%(function)s(%(expressions)s FROM '^\d+')" ), # 转换为整数 case_number_int=Cast('num_prefix', IntegerField()) ).order_by('case_number_int')
MySQL
from django.db.models import F, Func, Cast, IntegerField qs = MyObject.objects.annotate( num_prefix=Func( F('case_number'), function='REGEXP_SUBSTR', template=r"%(function)s(%(expressions)s, '^[0-9]+')" ), case_number_int=Cast('num_prefix', IntegerField()) ).order_by('case_number_int')
方案2:Python内存排序(仅适合小数据量场景)
如果你的数据量不大,可以直接将QuerySet转为列表后在Python层面处理排序,无需考虑数据库兼容性:
def get_sort_key(obj): # 提取所有数字字符转为整数 num_str = ''.join([c for c in obj.case_number if c.isdigit()]) return int(num_str) if num_str else 0 sorted_list = sorted(MyObject.objects.all(), key=get_sort_key)
注意:该方案会加载所有数据到内存,数据量超过1万条时不建议使用,且返回结果为Python列表不再支持QuerySet的链式操作。
方案3:新增预存字段(性能最优,适合高频排序场景)
如果该排序是业务高频操作,建议在模型中新增整数字段预存数字前缀,写入数据时自动计算:
from django.db import models class MyObject(models.Model): case_number = models.CharField(max_length=100) # 新增预存数字前缀的字段 case_number_int = models.IntegerField(editable=False, default=0) def save(self, *args, **kwargs): # 自动提取开头数字前缀 num_part = [] for c in self.case_number: if c.isdigit(): num_part.append(c) else: break self.case_number_int = int(''.join(num_part)) if num_part else 0 super().save(*args, **kwargs)
后续查询直接排序即可:
qs = MyObject.objects.all().order_by('case_number_int')
内容的提问来源于stack exchange,提问作者Hippolyte BRINGER
相关产品推荐
相关产品推荐

