Imagine a model Shirts
with a size
CharField, with values limited to a small number of choices, e.g. 'small', 'medium', 'large', 'xlarge' etc.
想象一下具有CharField大小的模特衬衫,其价值仅限于少数选择,例如: '小','中','大','xlarge'等
To get the shirts grouped by size, you'd do:
要按尺寸分组衬衫,您需要:
Shirts.objects.order_by('size')
But Django will (naturally) order the groups alphabetically, i.e. 'large' then 'medium' then 'small' then 'xlarge'. What I want is to have 'small' before 'medium' before 'large' etc.
但是Django会(自然地)按字母顺序对这些组进行排序,即'大'然后'中'然后'小'然后'xlarge'。我想要的是在'大'之前在'中'之前'小'等。
I.e. what I naturally want to do is something like the following pseudocode:
即我自然想要做的是类似下面的伪代码:
size_order = {'small': 1, 'medium': 2, 'large': 3, 'xlarge': 4}
Shirts.objects.order_by('size_order[size]')
What's the best way to accomplish this?
实现这一目标的最佳方法是什么?
EDIT: See my comments to answers below for thoughts on various suggested approaches. I've stumbled on a custom Manager/QuerySet approach using the SQL ORDER BY CASE syntax which I'm investigating.
编辑:请参阅我的评论以获取有关各种建议方法的想法。我使用我正在调查的SQL ORDER BY CASE语法偶然发现了一个自定义的Manager / QuerySet方法。
5 个解决方案
#1
8
I figured out the closest thing to what I'm looking for, which is to use QuerySet.extra()
method to take advantage of SQL's CASE WHEN/THEN syntax, which Django doesn't support directly:
我找到了与我正在寻找的最接近的东西,即使用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'])
This may well seem overkill and/or mucky given my (artificial) example, but it's the trick I was looking for! Thanks to everyone for the other perfectly valid approaches to this problem, which somehow indirectly sparked me to figure out this approach.
鉴于我的(人为的)例子,这可能看起来有点过分和/或肮脏,但这是我正在寻找的技巧!感谢大家对于这个问题的其他完全有效的方法,这在某种程度上间接地引发了我找出这种方法。
P.S. It's tempting to create a custom model Manager/QuerySet combo that provides a more native Django-interface for this sort of custom ordering via SQL's CASE WHEN/THEN syntax, but I'll leave that as a homework assignment for myself for another time!
附:创建一个自定义模型Manager / QuerySet组合很有吸引力,它通过SQL的CASE WHEN / THEN语法为这种自定义排序提供了更原生的Django接口,但是我将把它留给我自己作为另一个时间的家庭作业!
NOTE: The syntax for the CASE WHEN/THEN is database-specific. The syntax above is for SQLite. For PostgreSQL, omit the parentheses and use escaped single quotes instead of double quotes.
注意:CASE WHEN / THEN的语法是特定于数据库的。上面的语法适用于SQLite。对于PostgreSQL,省略括号并使用转义的单引号而不是双引号。
#2
2
You should set up your size
field with choice tuples ordered the way you want them. In your models.py
you'd have something like:
您应该设置您的大小字段,并选择按您希望的方式排序的元组。在你的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)
Then order_by
will sort them as you intend.
然后order_by会按照您的意愿对它们进行排序。
#3
2
It sounds like you don't want to hard-code the possible choices (because you used a charfield), but at the same time you say there are a small number of choices.
听起来你不想硬编码可能的选择(因为你使用了charfield),但同时你说有少量的选择。
If you are content to hard-code the choices then you could change to an integerfield instead:
如果您满足于对选择进行硬编码,那么您可以改为使用整数字段:
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)
If you don't want to hard-code the size choices then you probably want to move the available sizes out to a separate model and reference it as a foreignkey from your Shirt model. To make it arbitrarily sortable you would need an index of some sort other than the primary key that you can sort on. Maybe something like this:
如果您不想对大小选择进行硬编码,那么您可能希望将可用的大小移动到单独的模型中,并将其作为外键从衬衫模型中引用。要使其可以任意排序,您需要一个除了可以排序的主键之外的某种索引。也许是这样的:
class Size(models.Model):
sortorder = models.IntegerField()
name = models.CharField()
class Meta:
ordering = ['sortorder']
#4
2
Without writing a custom sorting function, just tack on an order
field to the model.
无需编写自定义排序功能,只需将订单字段添加到模型中即可。
class Shirt(models.Model):
...
order = models.IntegerField(default=0)
class Meta:
ordering = ('order',)
Shirt.objects.filter(name='Awesome Shirt')
Or, more appropriately, create a new model called Size
.
或者,更合适的是,创建一个名为Size的新模型。
#5
1
If you don't want to store the field values as integers, then the built in order_by() method won't be able to handle your custom case. You'll have to create a function of your own to sort the data once you've retrieved it.
如果您不想将字段值存储为整数,则内置的order_by()方法将无法处理您的自定义案例。您必须创建自己的函数,以便在检索数据后对其进行排序。
And to do that, of course the best way would be to map your arbitrary values to integers in respective order and then sort by that arrangement :).
要做到这一点,当然最好的方法是将您的任意值按各自的顺序映射到整数,然后按照这种安排排序:)。
#1
8
I figured out the closest thing to what I'm looking for, which is to use QuerySet.extra()
method to take advantage of SQL's CASE WHEN/THEN syntax, which Django doesn't support directly:
我找到了与我正在寻找的最接近的东西,即使用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'])
This may well seem overkill and/or mucky given my (artificial) example, but it's the trick I was looking for! Thanks to everyone for the other perfectly valid approaches to this problem, which somehow indirectly sparked me to figure out this approach.
鉴于我的(人为的)例子,这可能看起来有点过分和/或肮脏,但这是我正在寻找的技巧!感谢大家对于这个问题的其他完全有效的方法,这在某种程度上间接地引发了我找出这种方法。
P.S. It's tempting to create a custom model Manager/QuerySet combo that provides a more native Django-interface for this sort of custom ordering via SQL's CASE WHEN/THEN syntax, but I'll leave that as a homework assignment for myself for another time!
附:创建一个自定义模型Manager / QuerySet组合很有吸引力,它通过SQL的CASE WHEN / THEN语法为这种自定义排序提供了更原生的Django接口,但是我将把它留给我自己作为另一个时间的家庭作业!
NOTE: The syntax for the CASE WHEN/THEN is database-specific. The syntax above is for SQLite. For PostgreSQL, omit the parentheses and use escaped single quotes instead of double quotes.
注意:CASE WHEN / THEN的语法是特定于数据库的。上面的语法适用于SQLite。对于PostgreSQL,省略括号并使用转义的单引号而不是双引号。
#2
2
You should set up your size
field with choice tuples ordered the way you want them. In your models.py
you'd have something like:
您应该设置您的大小字段,并选择按您希望的方式排序的元组。在你的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)
Then order_by
will sort them as you intend.
然后order_by会按照您的意愿对它们进行排序。
#3
2
It sounds like you don't want to hard-code the possible choices (because you used a charfield), but at the same time you say there are a small number of choices.
听起来你不想硬编码可能的选择(因为你使用了charfield),但同时你说有少量的选择。
If you are content to hard-code the choices then you could change to an integerfield instead:
如果您满足于对选择进行硬编码,那么您可以改为使用整数字段:
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)
If you don't want to hard-code the size choices then you probably want to move the available sizes out to a separate model and reference it as a foreignkey from your Shirt model. To make it arbitrarily sortable you would need an index of some sort other than the primary key that you can sort on. Maybe something like this:
如果您不想对大小选择进行硬编码,那么您可能希望将可用的大小移动到单独的模型中,并将其作为外键从衬衫模型中引用。要使其可以任意排序,您需要一个除了可以排序的主键之外的某种索引。也许是这样的:
class Size(models.Model):
sortorder = models.IntegerField()
name = models.CharField()
class Meta:
ordering = ['sortorder']
#4
2
Without writing a custom sorting function, just tack on an order
field to the model.
无需编写自定义排序功能,只需将订单字段添加到模型中即可。
class Shirt(models.Model):
...
order = models.IntegerField(default=0)
class Meta:
ordering = ('order',)
Shirt.objects.filter(name='Awesome Shirt')
Or, more appropriately, create a new model called Size
.
或者,更合适的是,创建一个名为Size的新模型。
#5
1
If you don't want to store the field values as integers, then the built in order_by() method won't be able to handle your custom case. You'll have to create a function of your own to sort the data once you've retrieved it.
如果您不想将字段值存储为整数,则内置的order_by()方法将无法处理您的自定义案例。您必须创建自己的函数,以便在检索数据后对其进行排序。
And to do that, of course the best way would be to map your arbitrary values to integers in respective order and then sort by that arrangement :).
要做到这一点,当然最好的方法是将您的任意值按各自的顺序映射到整数,然后按照这种安排排序:)。