我想计算名称在查询集中出现的次数:
items = self.get_queryset()
items = items.values("name").annotate(count=Count("name")).order_by("-count")
其中 get_queryset() 返回带有一些过滤器和注释的项目列表。
def get_queryset(self):
return (
self.queryset.filter(...)
.annotate(...)
.order_by("id")
.distinct()
)
这应该返回如下查询集:
[
{
"name": "Item 1",
"count": 1
},
{
"name": "Item 2",
"count": 4
},
{
"name": "Item 3",
"count": 12
},
]
这会返回一些错误的计数,例如,应该出现 1 次的项目计数为 17,而应该出现 3 次的项目计数为 51,由于某种原因将其乘以 17。我该如何解决这个问题?
问题出在你对已经注释和过滤的查询集应用
values('name')
和
annotate(count=Count('name'))
的方式。 当你在已经注释的查询集上使用
values()
时,它会改变分组,导致计数不准确。
以下是解决方法以及原因:
问题:
-
初始注释和 distinct():
你的
get_queryset()
方法中的初始注释和.distinct()
可能会创建具有相同“名称”但其他字段不同的行。 -
values('name') 更改分组:
当你应用
values('name')
时,你将查询集分组到仅具有 "name" 字段的对象,从而丢弃其他注释。这会在初始注释和.distinct()
之前有效地对你的数据进行分组,从而导致计数不准确。
解决方案:
在使用
values('name')
之前
执行计数,或在子查询中执行计数:
方法 1:在 values() 之前计数
def get_queryset(self):
items = (
self.queryset.filter(...)
.values("name") # 首先按名称分组
.annotate(count=Count("id")) # 然后计算出现次数
.order_by("-count")
)
return items
方法 2:使用子查询
from django.db.models import Subquery, OuterRef
def get_queryset(self):
# 对初始查询集执行注释和过滤器
annotated_items = self.queryset.filter(...).annotate(...)
# 使用子查询计算每个名称的计数
items = (
self.queryset.filter(name=OuterRef('name'))
.values('name')
.annotate(count=Count('*'))
)
# 使用 Subquery 将计数注释到主查询集中
items = annotated_items.annotate(count=Subquery(items.values('count')[:1]))
return items.order_by("-count")
说明:
- 方法 1 通过首先对名称进行分组,然后计算每个名称的 id 数量来更正分组。
-
方法 2
使用子查询来计算每个名称的计数,而不会影响初始查询集的注释或过滤器。然后,使用
Subquery
将此计数应用于主查询集。
通过这些调整,你的计数应该会准确反映每个名称的出现次数。选择最适合你的代码结构和性能需求的方法。
标签:python,django From: 78785254