Django-order_by()字符和数值

时间:2019-02-20 15:10:26

标签: django postgresql django-queryset

我必须对包含主机名的对象列表进行排序。

主机名采用以下格式: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

实现这一目标的未来方法是什么?

2 个答案:

答案 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