我必须对包含主机名的对象列表进行排序。
主机名采用以下格式:h1, h5, h10, h12, h12-abc, h1000, x10
如果我使用order_by('hostname'),它将像这样订购:
h1, h10, h1000, h12, h12-abc, h5, x10
我如何实现这样的订购:
h1, h5, h10, h12, h12-abc, h1000, x10
主机名始终以字符开头,然后是1-4位数字,部分是扩展名,例如'-abc'。
我想我必须使用Substr()提取数字并以某种方式对数字进行排序,以使“ 10”不会在“ 5”之前列出。
通过搜索,我发现了一些带有extra()的旧示例,但是Django文档说它将在以后被弃用,并且“使用此方法作为最后的手段” https://docs.djangoproject.com/en/2.1/ref/models/querysets/#extra
实现这一目标的未来方法是什么?
答案 0 :(得分:4)
您可以使用f-expressions
from django.db.models import F, Value, TextField, IntegerField
from django.contrib.postgres.fields import ArrayField
from django.db.models.expressions import Func
sql = ordModel.objects.annotate(
num=Cast(
Func(
F('hostname'),
Value("\d+"),
function='regexp_matches',
),
output_field=ArrayField(IntegerField())
),
char=Func(
F('hostname'),
Value("\D+"),
function='regexp_matches',
output_field=ArrayField(TextField())
)
).order_by('char', 'num', ).values('hostname')
对于相同的值列表,我的结果是:
<QuerySet [
{'hostname': 'h1'},
{'hostname': 'h5'},
{'hostname': 'h10'},
{'hostname': 'h12'},
{'hostname': 'h12-abc'},
{'hostname': 'h1000'},
{'hostname': 'x10'}]>
关于您可以阅读的数据库功能L regexp_match
答案 1 :(得分:2)
我让它可以与一个额外的字段normalized_hostname
一起使用,该字段也与数据库无关。在模型中借助Django Signals pre_save()
https://docs.djangoproject.com/en/2.1/ref/signals/#pre-save
以下代码将主机名转换为一种格式,然后可以与order_by('normalized_hostname')
一起使用
示例:
主机名-> normalized_hostname
h1 -> h0001
h5 -> h0005,
h10 -> h0010
h12 -> h0012
h12-abc -> h0012-abc
h1000 -> h1000
x10 -> x0010
models.py
from django.db.models.signals import pre_save
import re
class MyModel(models.Model):
the solution is also database independent hostname = models.CharField(max_length=64)
normalized_hostname = models.CharField(max_length=64)
def create_normalize_hostname(instance):
normalize = re.sub("\D", "", instance.hostname).zfill(4)
normalized_hostname = re.sub("(\d{1,4})", normalize, instance.hostname)
return normalized_hostname
def receiver(sender, instance, *args, **kwargs)
instance.normalized_hostname = create_normalize_hostname(instance)
pre_save.connect(receiver, sender=ModelName)
现在它将像这样订购:
h1, h5, h10, h12, h12-abc, h1000, x10