Django注解来自ForeignKey模型的字段的SUM

fykwrbwg  于 2023-05-19  发布在  Go
关注(0)|答案(1)|浏览(109)

我在Django上写代码,遇到了一个问题。我有两个模特。

class Statistic(models.Model):
    campaign_id = models.CharField(max_length=255)
    adgroup_id = models.CharField(max_length=255)
    ad_id = models.CharField(max_length=255)
    campaign_name = models.CharField(max_length=255)
    adgroup_name = models.CharField(max_length=255)
    ad_name = models.CharField(max_length=255)
    date = models.DateField()
    spend = models.DecimalField(max_digits=10, decimal_places=2, default=0.0, )
    impressions = models.IntegerField(default=0)
    clicks = models.IntegerField(default=0)
    conversion = models.IntegerField(default=0)

class Tonic(models.Model):
    tiktok_statistic = models.ForeignKey(Statistic, on_delete=models.SET_NULL, null=True, related_name='tonic_statistic')
    campaign = models.ForeignKey(Campaigns, on_delete=models.CASCADE)
    date = models.DateField(max_length=256)
    keyword = models.CharField(max_length=255)
    clicks = models.IntegerField(default=0)
    revenueUsd = models.DecimalField(max_digits=9, decimal_places=2, default=0)
    id_campaign = models.CharField(max_length=255)
    id_adgroup = models.CharField(max_length=255)
    id_ad = models.CharField(max_length=255)

还有3个相关模型。

class Cross(models.Model):
tiktok_statistic = models.ForeignKey(Statistic, on_delete=models.SET_NULL, null=True,
                                     related_name='cross_statistic')
campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE)
date = models.DateField(max_length=256)
keyword = models.CharField(max_length=255, null=True)
clicks = models.IntegerField(default=0)
revenue = models.DecimalField(max_digits=9, decimal_places=2, default=0)
id_campaign = models.CharField(max_length=255)
id_adgroup = models.CharField(max_length=255)
id_ad = models.CharField(max_length=255)

我在Rest Framework中创建了endpoint。

class StatisticViewSet(ReadOnlyModelViewSet):
    queryset = Statistic.objects.all().values('campaign_name').prefetch_related(
    'tonic_statistic').annotate(
        spend=Sum('spend'),
        impressions=Sum('impressions'),
        clicks=Sum('clicks'),
        conversions=Sum('conversion'),
        revenue_tonic=Sum('tonic_statistic__revenueUsd'),
        revenue_cross=Sum('cross_statistic__revenue')
    ).order_by('-spend')

当我注解了花费,展示,点击和转换的SUM时,我得到的值是所需值的100-200倍。例如,我的支出应该是4,000,我得到了90000。我认为这是因为相关模型的价值被拉高了。
我可以设置distinct=True,然后值变得接近正确,但在这种情况下,一些数据丢失。
你能告诉我怎么修吗?也许我可以只从当前模型设置SPEND和其他字段,比如“self__spend”?

ffdz8vbo

ffdz8vbo1#

由于相关模型中的重复条目通过“tonic_statistic”和“cross_statistic”字段与Statistic模型连接,因此可能会计算出错误的值。
解决这个问题的一种方法是使用子查询分别计算相关模型的总和,然后将它们与主查询连接。下面是一个如何实现的示例:

from django.db.models import Subquery, OuterRef

tonic_subquery = TonicStatistic.objects.filter(
    campaign_name=OuterRef('campaign_name')
).values('campaign_name').annotate(
    revenue_tonic=Sum('revenueUsd')
).values('revenue_tonic')

cross_subquery = CrossStatistic.objects.filter(
    campaign_name=OuterRef('campaign_name')
).values('campaign_name').annotate(
    revenue_cross=Sum('revenue')
).values('revenue_cross')

queryset = Statistic.objects.annotate(
    revenue_tonic=Subquery(tonic_subquery),
    revenue_cross=Subquery(cross_subquery)
).values('campaign_name').annotate(
    spend=Sum('spend'),
    impressions=Sum('impressions'),
    clicks=Sum('clicks'),
    conversions=Sum('conversion'),
    revenue_tonic=Coalesce('revenue_tonic', 0),
    revenue_cross=Coalesce('revenue_cross', 0)
).order_by('-spend')

在这个例子中,我们首先定义两个子查询来分别计算相关模型的“revenueUsd”和“revenue”字段的总和。我们使用“OuterRef”表达式引用主查询的“campaign_name”字段,并使用“Coalesce”函数将空值转换为零。
然后,我们使用“Subquery”表达式用这些子查询来注解主查询,并使用“annotate”函数将它们与主查询连接起来。最后,我们像之前一样计算主查询中字段的总和。
这将为字段给予正确的和,同时避免相关模型中的任何重复条目。

相关问题