我遇到的情况是我必须通过用于存储街道地址的CharField输出相当大的对象列表。
我的问题是,显然数据是按ASCII码排序的,因为它是一个Charfield,具有可预测的结果..它对这样的数字进行排序;
1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21....
现在显而易见的一步是将Charfield更改为正确的字段类型(IntegerField),但是由于某些地址可能有公寓,所以它无法工作。如“128A”。
我真的不知道如何正确订购......
答案 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 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[] "
}
)
答案 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')
因此,MyObject
和name='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')
这样,无论如何,正确的分类对我有用。