gpt4 book ai didi

python - Django:具有过滤多对多的多个分组注释

转载 作者:行者123 更新时间:2023-12-03 23:47:03 24 4
gpt4 key购买 nike

我在 Django QuerySet 上遇到了一系列分组注释的问题,该问题由 ManyToMany 过滤。由于我一直盯着我的屏幕太久,让我们想象一系列关于品酒的模型:

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},
]

尝试 1

只是一些常规 valuesannotation
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},
]

嗯,所以看起来我遇到了与 multiple annotations 一样的问题。 -- 由于过滤事件时加入, wine_1之一行被计数两次:每个事件一次。

尝试 2

因此,查看来自 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},
]

哈扎!但是,如果我们将过滤器更改为仅包含事件 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表停止重复计数的疯狂。

Wine.objects.filter(
tasting__in=Tasting.event.through.objects.filter(event_id__in=[1, 2]).values('tasting_id')
).values('id').annotate(
total_score=Sum('tasting__score'),
average_scores=Avg('tasting__score')
)
[
{'id': 1, 'total_score': 150, 'average_scores': 50.0},
{'id': 2, 'total_score': 250, 'average_scores': 125.0},
{'id': 3, 'total_score': 75, 'average_scores': 75.0}
]

关于python - Django:具有过滤多对多的多个分组注释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62074349/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com