在Django中,如何按任意而非字母顺序的多选字符域排序?

在Django中,如何按任意而非字母顺序的多选字符域排序?,django,django-models,sql-order-by,Django,Django Models,Sql Order By,设想一个型号衬衫,带有尺寸字符域,其值仅限于少量选择,例如“小”、“中”、“大”、“xlarge”等 要按尺寸对衬衫进行分组,请执行以下操作: Shirts.objects.order_by('size') 但Django(自然)会按字母顺序排列,即“大”然后“中”然后“小”然后“大”。我想要的是在“大”之前的“中”之前的“小”等等 也就是说,我自然想做的是类似以下伪代码的事情: size_order = {'small': 1, 'medium': 2, 'large': 3, 'xlarg

设想一个型号
衬衫
,带有
尺寸
字符域,其值仅限于少量选择,例如“小”、“中”、“大”、“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按大小写顺序语法。

您应该设置
大小
字段,选择元组按您希望的方式排序。在您的
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\u by
将按照您的意愿对它们进行排序。

听起来您不想硬编码可能的选择(因为您使用了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']

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


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

无需编写自定义排序函数,只需将
顺序
字段添加到模型中即可

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

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

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

的新模型。我找到了最接近我要寻找的东西,那就是使用
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'])
考虑到我的(人为的)例子,这可能看起来有些过分和/或肮脏,但这正是我一直在寻找的技巧!感谢大家为解决这个问题提供了其他非常有效的方法,这间接地激发了我对这个方法的思考

另外,通过SQL的CASE-WHEN/THEN语法,创建一个定制模型管理器/QuerySet组合,为这种定制排序提供一个更为原生的Django接口,这很有诱惑力,但我将把它作为我的家庭作业留到下一次


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

是的,我不想用这种方式硬编码它们。(顺便说一句,这个衬衫示例只是一个人工示例。)我真的希望有一种更简单的方法,可以按照字段的“映射”排序,而不必像您建议的那样使用单独的表,但也许没有……如果您不想硬编码可用的选项,那么您必须将它们移动到一个单独的模型中,就像我上面建议的那样。这是你唯一的选择。我想出了另一个选择:-)。请看我的答案。谢谢,但出于各种原因,我希望字段值本身是“小”的,等等。(请注意,这个示例只是一个人工示例。)啊。。。您的意思是创建一个自定义模型管理器,该管理器返回一个自定义QuerySet子类,该子类具有一个新的order_by_map()(或其他任何形式),可以满足我的需要吗?如果是的话,这是有道理的。对于我上面给出的这个愚蠢的例子来说,这可能有些过头了,但如果这样做有效的话,这就是我要寻找的。好的,看起来我需要为SQL ORDER BY CASE添加一个QuerySet方法。不确定是否有标准的SQL语法,当然也不确定它是否是特定于DB的。。。我正在调查。哦,等等,我看到你不是指定制管理器/查询集——只是在查询后对结果进行排序。对,这绝对是一个选择,但不是我想要的。出于某种原因,您的回答引发了对客户经理/查询集的思考,所以无论如何,谢谢!哈哈,很高兴我能帮上忙。好的解决方案btwThanks,这很有意义,但我希望避免创建另一个字段或表。(
Shirts
只是一个愚蠢的例子。)请参阅我的答案,了解我是如何解决这个问题的。实际上,通过
extra()
将表达式注入QuerySet时,使用SQL CASE/THEN动态创建一个自定义排序字段。可能重复