想象一个带有Shirts
CharField的模型size
,其值仅限于少数选项,例如'小','中','大','xlarge'等。
要按尺寸分组衬衫,您可以:
Shirts.objects.order_by('size')
但是Django会(自然地)按字母顺序对这些组进行排序,即“大”然后“中等”然后“小”然后“xlarge”。我想要的是在'大'之前'中'之前有'小'等等。
即。我自然想要做的是类似下面的伪代码:
size_order = {'small': 1, 'medium': 2, 'large': 3, 'xlarge': 4}
Shirts.objects.order_by('size_order[size]')
实现这一目标的最佳方法是什么?
编辑:请参阅我对以下答案的评论,以了解各种建议的方法。我使用我正在调查的SQL ORDER BY CASE语法偶然发现了一个自定义的Manager / QuerySet方法。
答案 0 :(得分:9)
我找到了与我正在寻找的最接近的东西,即使用QuerySet.extra()
方法来利用SQL的CASE WHEN / THEN语法,Django不直接支持:
CASE_SQL = '(case when size="small" then 1 when size="medium" then 2 when size="large" then 3 when size="xlarge" then 4 end)'
Shirt.objects.extra(select={'shirt_order': CASE_SQL}, order_by=['shirt_order'])
考虑到我的(人为)示例,这看起来似乎有点过分和/或肮脏,但这是我正在寻找的技巧!感谢大家对这个问题的其他完全有效的方法,这在某种程度上间接地引发了我找出这种方法。
P.S。创建一个自定义模型Manager / QuerySet组合很有吸引力,它通过SQL的CASE WHEN / THEN语法为这种自定义排序提供了更原生的Django接口,但我将把它留给我自己作为另一个时间的家庭作业!
注意:CASE WHEN / THEN的语法是特定于数据库的。上面的语法适用于SQLite。对于PostgreSQL,省略括号并使用转义的单引号而不是双引号。
答案 1 :(得分:2)
您应该按照您希望的方式设置选择元组的size
字段。在你的models.py
中你会有类似的东西:
from django.db import models
SHIRT_SIZE_CHOICES = (
(u"0", u"small"),
(u"1", u"medium"),
(u"2", u"large"),
(u"3", u"xlarge"))
class Shirt(models.Model):
...
size = models.CharField(max_length=2, choices=SHIRT_SIZE_CHOICES)
然后order_by
会根据您的意愿对它们进行排序。
答案 2 :(得分:2)
听起来你不想对可能的选择进行硬编码(因为你使用了charfield),但与此同时你说有少量的选择。
如果您满足于对选项进行硬编码,那么您可以改为使用整数字段:
class Shirt(models.Model):
SIZE_CHOICES = (
(1, u'small'),
(2, u'medium'),
(3, u'large'),
(4, u'x-large'),
(5, u'xx-large'),
)
size = models.IntegerField(choices = SIZE_CHOICES)
如果您不想对尺寸选择进行硬编码,那么您可能希望将可用尺寸移到单独的模型中,并将其作为衬衫模型中的外键引用。要使其可以任意排序,您需要一个除了可以排序的主键之外的某种索引。也许是这样的:
class Size(models.Model):
sortorder = models.IntegerField()
name = models.CharField()
class Meta:
ordering = ['sortorder']
答案 3 :(得分:2)
无需编写自定义排序功能,只需将order
字段添加到模型中即可。
class Shirt(models.Model):
...
order = models.IntegerField(default=0)
class Meta:
ordering = ('order',)
Shirt.objects.filter(name='Awesome Shirt')
或者更恰当地说,创建一个名为Size
的新模型。
答案 4 :(得分:1)
如果您不想将字段值存储为整数,则内置的order_by()方法将无法处理您的自定义案例。您必须创建自己的函数,以便在检索数据后对其进行排序。
要做到这一点,当然最好的方法是将您的任意值按各自的顺序映射到整数,然后按照这种安排排序:)。