我有PhysicalServer
型号:
class PhysicalServer(models.Model):
name = models.CharField(max_length=32)
cabinet = models.ForeignKey(to=Cabinet, on_delete=models.DO_NOTHING, related_name="physical_servers")
physical_server_model = models.ForeignKey(to=PhysicalServerModel, null=True, on_delete=models.DO_NOTHING)
...
class Meta:
ordering = ['-cabinet', '-physical_server_model', 'name']
其列表API视图是:
class PhysicalServerListAPIView(ListAPIView):
serializer_class = PhysicalServerListSerializer
permission_classes = [AllowAny]
pagination_class = CommonPagination
def get_queryset(self):
qs = PhysicalServer.objects.filter(**filters)
return qs.annotate(length=Length('name')).order_by('length', 'name') # there if I put the `name` first(order_by('name', 'length')), also inconformity my requirement.
我的物理服务器实例名称如下所示:
我的问题是,当我使用它进行列表排序时:
return qs.annotate(length=Length('name')).order_by('length', 'name')
结果将是:
SE01-A1
SE01-A2
SE01-A3
...
SE01-A9
SE01-C1
SE01-C2
SE01-C3
...
SE01-A10
SE01-A11
SE01-A12
...
如果我使用以下内容进行排序:
return qs.annotate(length=Length('name')).order_by('name', 'length')
结果将是:
SE01-A1
SE01-A11
SE01-A12
SE01-A13
...
SE01-A2
SE01-A21
...
SE01-A3
...
我怎么这样排序:
SE01-A1
SE01-A2
SE01-A3
SE01-A4
...
SE01-A10
...
SE01-C1
SE01-C2
...
答案 0 :(得分:2)
您需要从字符串中提取数字。 Postgres和mysql提供regexp_replace
函数(我不确定其他数据库)。但是Django没有提供实现,因此我们将编写自己的function:
from django.db.models import Func, Value
class RegexpReplace(Func):
function = 'REGEXP_REPLACE'
def __init__(self, expression, search, replace, **extra):
search = Value(search)
replace = Value(replace)
super(RegexpReplace, self).__init__(expression, search, replace, **extra)
我假设您要分割名称直到末尾的数字,然后再使用前半部分和末尾的数字进行排序。 (这对您来说很好,直到您开始在连字符前得到3位数字,即SE99-A1,SE100-A1)。
from django.db.models import F, IntegerField
from django.db.models.functions import Cast
qs = ... # get your queryset
qs.annotate(
letters=RegexpReplace(F('name'), '(.*[a-zA-Z])[0-9]+$', r'\1'),
seq=Cast(
RegexpReplace(F('name'), '.*[a-zA-Z]([0-9]+)$', r'\1'),
IntegerField(),
),
).order_by('letters', 'seq')
答案 1 :(得分:0)
如果您的服务器名称始终为server-code
形式且没有其他连字符出现,您可以拆分连字符并按如此排序:
qs = sorted(qs, key: lambda i: (i.name().split('-')[1], len(i.name())))
注意:这不会返回查询集。
答案 2 :(得分:-1)
使用Substr将名称的第一部分与末尾的数字分开,然后先按此新注释排序。
from django.db.models.functions import Substr, Length
qs = qs.annotate(letters=Substr('name', 1, 6), length=Length('name'))
qs = qs.order_by('letters', 'length', 'name')
return qs