基本上,我需要根据来自 ManyToManyField 的多个值进行订购。
所以我想要实现的是将具有最多质疑值的对象放在最上面,继续使用具有较少质疑值的对象。继续使用没有任何这些值的对象。
型号:
class Color(models.Model):
name = models.CharField(max_length=200)
class Item(models.Model):
surface_color = models.ManyToManyField(Color)
基于上述模型创建的实例:
现在我需要订购基于多种颜色:
假查询:
Item.objects.all().order_by(surface_color_id=[1, 3])
查询应具有以下结果:
这可以通过单个查询集实现吗?或者我是否需要为每个组合发送多个查询?
我在互联网上发现的唯一内容是关于订购多个 字段 ,这是关于 值 。
任何帮助表示赞赏。
最佳答案
这应该给你你想要的:
list(Item.objects.filter(surface_color__in=[1,3]).distinct().annotate(num_colors=Count('surface_color')).order_by('-num_colors')) + list(Item.objects.exclude(surface_color__in=[1,3]).distinct())
它需要两个查询,但您不需要为每个 Item 单独查询。
用评论分解相同的逻辑:
# Make sure you import Count somewhere in the file first
from django.db.models import Count
# id of Color objects to match
color_ids_to_match = [1,3]
#-----------------------------------------------------------------------------------------------------------------------
# Item objects with Color objects matching `color_ids_to_match`
# - The `surface_color` field has **at least one** Color object with a matching id from `color_ids_to_match`
# - This matches Items #4, #1, #2 from your sample data
items_with_matching_color = Item.objects.filter(surface_color__in=color_ids_to_match).distinct()
# Adds a select field to the query to track the number of surface_color objects matched
# - **NOT the total** number of Color objects associated through `surface_color`
items_with_matching_color = items_with_matching_color.annotate(num_colors=Count('surface_color'))
# Order by that field in descending order
# - Note that the order is undetermined between Item objects with the same `num_colors` value
items_with_matching_color = items_with_matching_color.order_by('-num_colors')
#-----------------------------------------------------------------------------------------------------------------------
# Item objects **NOT** associated with any Color objects with a id in `color_ids_to_match`
# - This matches Item #3 from your sample data
items_without_matching_color = Item.objects.exclude(surface_color__in=color_ids_to_match).distinct()
# Optional - Sets the num_colors field to 0 for this queryset in case you need this information
from django.db.models.expressions import RawSQL
items_without_matching_color = items_without_matching_color.annotate(num_colors=RawSQL('0', ()))
#-----------------------------------------------------------------------------------------------------------------------
# Convert the two querysets to lists and concatenate them
# - This is necessary because a simple queryset union such as `items_with_matching_color | items_without_matching_color`
# does not maintain the order between the two querysets
ordered_items = list(items_with_matching_color) + list(items_without_matching_color)
使用您的示例数据输出:
>>> ordered_items
[<Item: 1: <QuerySet [<Color: 1>, <Color: 2>, <Color: 3>]>>, <Item: 4: <QuerySet [<Color: 1>, <Color: 3>]>>, <Item: 2: <QuerySet [<Color: 1>, <Color: 2>]>>, <Item: 3: <QuerySet [<Color: 2>]>>]
请注意,这里的第 1 项在第 4 项之前。您提到两者之间的顺序无关紧要,因为它们都匹配相同数量的 Color 对象。您可以向
order_by
添加另一个参数,以根据您的需要进一步细化排序。获取匹配 Color 对象的数量:
<Item: 1: <QuerySet [<Color: 1>, <Color: 2>, <Color: 3>]>>
>>> ordered_items[0].num_colors
2
>>> ordered_items[1]
<Item: 4: <QuerySet [<Color: 1>, <Color: 3>]>>
>>> ordered_items[1].num_colors
2
>>> ordered_items[2]
<Item: 2: <QuerySet [<Color: 1>, <Color: 2>]>>
>>> ordered_items[2].num_colors
1
>>> ordered_items[3]
<Item: 3: <QuerySet [<Color: 2>]>>
>>> ordered_items[3].num_colors
0
关于django - 如何根据 ManyToManyField 中的多个值订购模型?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37674471/