在Django中,如何通过多选CharField以任意非字母顺序排序?

时间:2012-07-27 21:38:10

标签: django django-models sql-order-by

想象一个带有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方法。

5 个答案:

答案 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()方法将无法处理您的自定义案例。您必须创建自己的函数,以便在检索数据后对其进行排序。

要做到这一点,当然最好的方法是将您的任意值按各自的顺序映射到整数,然后按照这种安排排序:)。