11

想象一个Shirts带有sizeCharField 的模型,其值仅限于少数选择,例如“small”、“medium”、“large”、“xlarge”等。

要按尺寸对衬衫进行分组,您可以:

Shirts.objects.order_by('size')

但是 Django 会(自然地)按字母顺序对组进行排序,即“大”然后“中”然后“小”然后“超大”。我想要的是在“大”之前的“中”之前有“小”等。

即我自然想做的是类似于以下伪代码:

size_order = {'small': 1, 'medium': 2, 'large': 3, 'xlarge': 4}
Shirts.objects.order_by('size_order[size]')

实现这一目标的最佳方法是什么?

编辑:有关各种建议方法的想法,请参阅我对下面答案的评论。我偶然发现了一种使用我正在调查的 SQL ORDER BY CASE 语法的自定义 Manager/QuerySet 方法。

4

5 回答 5

11

我想出了最接近我正在寻找的东西,即使用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'])

鉴于我的(人工)示例,这可能看起来有点矫枉过正和/或肮脏,但这是我正在寻找的技巧!感谢大家对这个问题的其他完全有效的方法,这在某种程度上间接地激发了我想出这种方法。

PS 创建一个自定义模型 Manager/QuerySet 组合是很诱人的,它通过 SQL 的 CASE WHEN/THEN 语法为这种自定义排序提供更原生的 Django 接口,但我将把它作为自己的家庭作业留待下一次!

注意:CASE WHEN/THEN 的语法是特定于数据库的。上面的语法适用于 SQLite。对于 PostgreSQL,省略括号并使用转义单引号而不是双引号。

于 2012-07-28T13:45:05.373 回答
3

听起来您不想对可能的选择进行硬编码(因为您使用了字符域),但同时您说选择的数量很少。

如果您满足于对选择进行硬编码,那么您可以改为使用整数字段:

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)

如果您不想对尺寸选择进行硬编码,那么您可能希望将可用尺寸移到单独的模型中,并将其作为 Shirt 模型中的外键引用。要使其可任意排序,您将需要某种类型的索引,而不是可以排序的主键。也许是这样的:

class Size(models.Model):
  sortorder = models.IntegerField()
  name = models.CharField()
  class Meta:
    ordering = ['sortorder']
于 2012-07-27T22:07:02.647 回答
2

无需编写自定义排序功能,只需order在模型上添加一个字段。

class Shirt(models.Model):
    ...
    order = models.IntegerField(default=0)
    class Meta:
        ordering = ('order',)

Shirt.objects.filter(name='Awesome Shirt')

或者,更恰当地说,创建一个名为Size.

于 2012-07-28T09:51:22.630 回答
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将按照您的意愿对它们进行排序。

于 2012-07-27T21:58:48.220 回答
1

如果您不想将字段值存储为整数,那么内置的 order_by() 方法将无法处理您的自定义案例。检索数据后,您必须创建自己的函数来对数据进行排序。

而要做到这一点,当然最好的方法是将您的任意值以相应的顺序映射到整数,然后按该排列排序:)。

于 2012-07-28T09:45:55.240 回答