Django:使用order_by排序数值

时间:2010-06-03 20:03:01

标签: django sorting

我遇到的情况是我必须通过用于存储街道地址的CharField输出相当大的对象列表。

我的问题是,显然数据是按ASCII码排序的,因为它是一个Charfield,具有可预测的结果..它对这样的数字进行排序;

1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21....

现在显而易见的一步是将Charfield更改为正确的字段类型(IntegerField),但是由于某些地址可能有公寓,所以它无法工作。如“128A”。

我真的不知道如何正确订购......

8 个答案:

答案 0 :(得分:19)

如果您确定该字段中只有整数,您可以通过extra方法将数据库转换为整数,并按顺序排序:

MyModel.objects.extra(
    select={'myinteger': 'CAST(mycharfield AS INTEGER)'}
).order_by('myinteger')

答案 1 :(得分:12)

如果您正在使用PostgreSQL(不确定MySQL),您可以安全地在字符/文本字段上使用以下代码并避免出现错误:

MyModel.objects.extra(
    select={'myinteger': "CAST(substring(charfield FROM '^[0-9]+') AS INTEGER)"}
).order_by('myinteger')

答案 2 :(得分:9)

Django正在尝试deprecate extra()方法,但在v1.10中引入了Cast()。在sqlite(至少)中,CAST可以使用10a之类的值并将其转换为整数10,因此您可以这样做:

from django.db.models import IntegerField
from django.db.models.functions import Cast

MyModel.objects.annotate(
    my_integer_field=Cast('my_char_field', IntegerField())
).order_by('my_integer_field', 'my_char_field')

将首先以数字方式返回按街道号排序的对象,然后按字母顺序返回,例如...14, 15a, 15b, 16, 16a, 17...

答案 3 :(得分:3)

很棒的提示!这个对我有用! :)那是我的代码:

revisioned_objects = revisioned_objects.extra(select={'casted_object_id': 'CAST(object_id AS INTEGER)'}).extra(order_by = ['casted_object_id'])

答案 4 :(得分:2)

您遇到的问题与按文件名排序时文件名的排序方式非常相似。在那里,你想要“2 Foo.mp3”出现在“12 Foo.mp3”之前。

一种常见的方法是“规范化”数字以扩展到固定的数字位数,然后根据规范化的形式进行排序。也就是说,出于排序的目的,“2 Foo.mp3”可能会扩展为“0000000002 Foo.mp3”。

Django不会直接帮助你。您可以添加一个字段来存储“规范化”地址,并拥有数据库order_by,或者您可以在地址记录中对视图(或视图中使用的帮助程序)进行自定义排序模板的记录列表。

答案 5 :(得分:1)

如果您需要对由点分隔的多个数字组成的版本号进行排序(例如1.9.0, 1.10.0),这里只是一个postgres解决方案:

class VersionRecordManager(models.Manager):

    def get_queryset(self):
        return super().get_queryset().extra(
            select={
                'natural_version': "string_to_array(version, '.')::int[]",
            },
        )

    def available_versions(self):
        return self.filter(available=True).order_by('-natural_version')

    def last_stable(self):
        return self.available_versions().filter(stable=True).first()

class VersionRecord(models.Model):
    objects = VersionRecordManager()
    version = models.CharField(max_length=64, db_index=True)
    available = models.BooleanField(default=False, db_index=True)
    stable = models.BooleanField(default=False, db_index=True)

如果您想允许使用非数字字符(例如0.9.0 beta2.0.0 stable):

def get_queryset(self):
    return super().get_queryset().extra(
        select={
            'natural_version':
                "string_to_array(                     "  
                "   regexp_replace(                   "  # Remove everything except digits
                "       version, '[^\d\.]+', '', 'g'  "  # and dots, then split string into
                "   ), '.'                            "  # an array of integers.
                ")::int[]                             "
        }
    )

答案 6 :(得分:0)

我一直在寻找一种对CharField中的数字字符进行排序的方法,而我的搜索却把我引到了这里。我对象中的name字段是CC许可,例如“ CC BY-NC 4.0”。

由于extra()将被弃用,因此我可以这样操作:

MyObject.objects.all()
    .annotate(sorting_int=Cast(Func(F('name'), Value('\D'), Value(''), Value('g'), function='regexp_replace'), IntegerField()))
    .order_by('-sorting_int')

因此,MyObjectname='CC BY-NC 4.0'现在有了sorting_int=40

答案 7 :(得分:0)

在我的情况下,我有一个带有名称字段的CharField,例如,该字段具有混合(int +字符串)值。 “ a1”,“ f65”,“ P”,“ 55” e.t.c ..

使用sql cast解决了该问题(已通过postgres和mysql测试), 首先,我尝试按强制转换的整数值排序,然后按名称字段的原始值排序。

parking_slots = ParkingSlot.objects.all().extra(
        select={'num_from_name': 'CAST(name AS INTEGER)'}
    ).order_by('num_from_name', 'name')

这样,无论如何,正确的分类对我有用。