我面临的情况是,我必须通过用于存储街道地址的 CharField 输出相当大的对象列表。
我的问题是,显然数据是按 ASCII 代码排序的,因为它是 Charfield,具有可预测的结果..它像这样对数字进行排序;
1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21....
现在显而易见的步骤是将 Charfield 更改为正确的字段类型(比如说 IntegerField),但是它无法工作,因为某些地址可能有公寓..比如“128A”。
我真的不知道如何正确订购这个..
如果您确定该字段中只有整数,您可以通过
extra
方法让数据库将其转换为整数,并按以下顺序排序:
MyModel.objects.extra(
select={'myinteger': 'CAST(mycharfield AS INTEGER)'}
).order_by('myinteger')
Django 正在尝试弃用
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...
如果您使用 PostgreSQL(不确定 MySQL),您可以在 char/text 字段上安全地使用以下代码并避免转换错误:
MyModel.objects.extra(
select={'myinteger': "CAST(substring(charfield FROM '^[0-9]+') AS INTEGER)"}
).order_by('myinteger')
我知道我迟到了,但因为它与问题密切相关,而且我很难找到这个:
您要知道,您可以直接将
Cast
放入您模型的ordering
选项中。
from django.db import models
from django.db.models.functions import Cast
class Address(models.Model):
street_number = models.CharField()
class Meta:
ordering = [
Cast("street_number", output_field=models.IntegerField()),
]
来自关于订购的文档:
您还可以使用查询表达式。
来自关于数据库函数的文档:
函数也是表达式,因此它们可以与聚合函数等其他表达式一起使用和组合。
很棒的提示!这个对我有用! :) 这是我的代码:
revisioned_objects = revisioned_objects.extra(select={'casted_object_id': 'CAST(object_id AS INTEGER)'}).extra(order_by = ['casted_object_id'])
您遇到的问题与按文件名排序时文件名的排序方式非常相似。在那里,您希望“2 Foo.mp3”出现在“12 Foo.mp3”之前。
常见的做法是将数字“规范化”,扩展为固定位数,然后根据规范化形式进行排序。也就是说,为了排序,“2 Foo.mp3”可能会扩展为“0000000002 Foo.mp3”。
Django 不会直接帮助你。您可以添加一个字段来存储“标准化”地址,并拥有数据库
order_by
,或者您可以在提交列表之前在视图中(或在视图使用的帮助程序中)对地址记录进行自定义排序记录到模板。
在我的例子中,我有一个带有名称字段的 CharField,例如,它具有混合(int + string)值。 “a1”、“f65”、“P”、“55”等 ..
通过使用 sql 转换解决了问题(使用 postgres 和 mysql 测试), 首先,我尝试按转换的整数值排序,然后按名称字段的原始值排序。
parking_slots = ParkingSlot.objects.all().extra(
select={'num_from_name': 'CAST(name AS INTEGER)'}
).order_by('num_from_name', 'name')
这样,无论如何,正确的排序对我来说都是有效的。
如果您需要对由点分隔的多个数字组成的版本号进行排序(例如
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 beta
、2.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[] "
}
)
我正在寻找一种对
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')
因此,
MyObject
和 name='CC BY-NC 4.0'
现在有 sorting_int=40
。
该线程中的所有答案对我来说都不起作用,因为它们假设是数字文本。我找到了一个适用于部分情况的解决方案。考虑这个模型
Class Block(models.Model):
title = models.CharField()
假设我的字段有时有前导字符和尾随数字字符如果我尝试正常订购
>>> Block.objects.all().order_by('title')
<QuerySet [<Block: 1>, <Block: 10>, <Block: 15>, <Block: 2>, <Block: N1>, <Block: N12>, <Block: N4>]>
正如预期的那样,它按字母顺序排列是正确的,但对我们人类来说毫无意义。我针对这个特定用例所做的技巧是将我找到的任何文本替换为数字 9999,然后将该值转换为整数并按它排序。
对于大多数具有主角的情况,这将获得所需的结果。见下文
from django.db.models.expressions import RawSQL
>>> Block.objects.all()\
.annotate(my_faux_integer=RawSQL("CAST(regexp_replace(title, '[A-Z]+', '9999', 'g') AS INTEGER)", ''))\
.order_by('my_faux_integer', 'title')
<QuerySet [<Block: 1>, <Block: 2>, <Block: 10>, <Block: 15>, <Block: N1>, <Block: N4>, <Block: N12>]>
我也有类似的情况。旧数据库有一个表,其中的列(名为
page
)中主要包含数字数据,类型为 varchar
。我需要按自然数字顺序对从该表查询的结果进行排序,但不需要更改结果中的数据类型。我最终将列值转换为数字仅用于排序:
(ItemPage.objects.filter(volume__id=volumeId)
.order_by('item__topic__name', F('page') * 1, 'item__name'))
我在这里看到一些其他答案表明MySQL的
CAST()
可能不适用于包含数字和字母混合的字符串,但从实验来看,我认为这不是真的。也许这是旧版本 MySQL 的问题。
无论这是否是一个问题,我决定将值乘以一。这对我来说很有效。在某个时候,我会尝试使用
CAST()
,看看它是否总是有效,以及它的执行速度是否比乘以一更快。