我遇到了一个问题,Django QuerySet上的一系列分组注释正在被许多人过滤。由于我盯着屏幕看了太久,让我们想象一下一系列关于品酒的模型:
class Event(model.Model):
# Some Fields
class Wine(models.Model):
# Some Fields
class Tasting(models.Model):
event = models.ManyToManyField(Event)
wine = models.ForeignKey(Wine)
score = models.IntegerField()
我想通过wine对数据进行一些聚合,可以选择对某些事件进行过滤。使用此示例品尝数据(我将在其上运行聚合):
| wine_id | score | event_ids |
| ------- | ----- | --------- |
| 1 | 50 | [1] |
| 1 | 50 | [1] |
| 1 | 50 | [1, 2] |
| 2 | 100 | [1, 2] |
| 2 | 150 | [1, 2] |
| 3 | 75 | [1] |
上述数据的预期输出为:
[
{'wine_id': 1, 'total_scores': 150, 'average_scores': 50},
{'wine_id': 2, 'total_scores': 250, 'average_scores': 125},
{'wine_id': 3, 'total_scores': 75, 'average_scores': 75},
]
只是一些普通的values
和annotation
Tasting.objects.filter(
event__in=Event.objects.filter(id__in=[1,2])
).distinct().values('wine_id').annotate(
total_scores=Sum('score'),
average_scores=Avg('scores'),
)
哪些产出:
[
{'wine_id': 1, 'total_scores': 200, 'average_scores': 50}, # Total score too high
{'wine_id': 2, 'total_scores': 250, 'average_scores': 125},
{'wine_id': 3, 'total_scores': 75, 'average_scores': 75},
]
Hrm,所以看起来我遇到了与multiple annotations相同的问题——由于过滤事件时的连接,wine_1
行中的一行被计数两次:每个事件一次
因此,从Django问题(例如this answer)的一系列建议来看,我认为我可以用子查询来解决这个问题,这让我想到了这个问题:
total_subquery = Subquery(Tasting.objects.filter(wine_id=OuterRef('wine_id')).annotate(
total_scores=Sum('score'),
).values('total_scores'))
average_subquery = Subquery(Tasting.objects.filter(wine_id=OuterRef('wine_id')).annotate(
average_scores=Avg('scores'),
).values('average_scores'))
Tasting.objects.filter(
event__in=Event.objects.filter(id__in=[1,2])
).distinct().values('wine_id').annotate(
total_scores=total_subquery,
average_scores=average_subquery,
)
因此,一开始,这看起来是正确的:
[
{'wine_id': 1, 'total_scores': 150, 'average_scores': 50},
{'wine_id': 2, 'total_scores': 250, 'average_scores': 125},
{'wine_id': 3, 'total_scores': 75, 'average_scores': 75},
]
Huzzah!但是,如果我们将筛选器更改为仅包含事件2,该怎么办
Tasting.objects.filter(
event__in=Event.objects.filter(id__in=[2])
).distinct().values('wine_id').annotate(
total_scores=total_subquery,
average_scores=average_subquery,
)
在这种情况下,我仍然会返回所有事件的数据。这很直观,因为子查询不知道外部过滤器。但是,如果我更改子查询上的OuterRef
值(类似于filter(pk=OuterRef('pk'))
)如果我在子查询级别重新添加事件筛选,那么我们将得到与第一次尝试相同的重复行问题
我可以通过简单地获取所有数据,然后用Python进行聚合来获得正确的值,但是这会给较大的数据集带来严重的性能代价。有没有一种方法可以完全通过ORM进行聚合
希望你不要太沮丧:)
在这种情况下,您需要做的是避免
Events
表的联接,以停止重复计数的疯狂行为相关问题 更多 >
编程相关推荐