【问题标题】:How to compute Sum of an aggregate with Django?如何用 Django 计算聚合的总和?
【发布时间】:2020-01-30 11:11:24
【问题描述】:

我目前正在尝试在 SQL 端计算 Surveyscore,以便能够按他们的分数排序调查。

我现在的逻辑是:

  • 计算我的Answer的实系数
  • 为我的Question 计算该系数的总和(Answer 可以有多个,所以我使用 Sum)
  • 基本上根据所有Question点的总和(Question.point * sum(Answer.coef))计算我整个Surveypoints的数量
    Survey.objects.annotate(
            answerresponse__realcoef=models.Case(
                models.When(answerresponse__coef__isnull=True,
                            then=models.F('answerresponse__answer__coef')),
                models.When(answerresponse__coef__isnull=False,
                            then=models.F('answerresponse__coef')),
                output_field=models.FloatField(),
            )
            ).annotate(
                answerresponse__realcoef_sum=models.Sum(
                    models.F('answerresponse__realcoef')
                )
            ).annotate(
                points=models.Sum(
                    models.F('answerresponse__realcoef_sum') *
                    models.F('answerresponse__answer__question__points'),
                    output_field=models.IntegerField()
                ),
                maxpoints=models.Sum('sections__question__points')
            )

数据库架构类似于:

Survey > Sections > Questions (points) > Answer (coef) > AnswerResponse (coef override)

我收到以下错误:

FieldError: 无法计算 Sum(''): '' 是一个聚合

我理解为

那个 SQL 部分还没有执行,所以你不能依赖它

是否可以通过仅保留 SQL 端来实现?

【问题讨论】:

    标签: python django orm django-annotate


    【解决方案1】:

    您可以尝试先将answerresponse__realcoef_sum * answerresponse__answer__question__points 的结果用expression wrapper 注释:

    .annotate(
        total=ExpressionWrapper(
            F('answerresponse__realcoef_sum') * F('answerresponse__answer__question__points'), output_field=IntegerField())
    )
    

    然后通过SUM聚合结果:

    .annotate(points=models.Sum('total'))
    

    【讨论】:

    • 感谢您的建议,它没有用,因为我认为将结果放入 answerresponse__ 正在存储 ONE 响应的值,现在我知道这是汇总的,所以我正在尝试一种方法2个请求:一个注释AnswerResponse以将每个响应的点数放入子查询中,但我无法正确使用它,因为我有“子查询必须只返回一列”(我试图从一个子查询)
    【解决方案2】:

    我能够找到解决方案:我正在使用子查询来计算每个调查的总数,然后注释到调查查询集中,OuterRef 方法允许我将父请求 pk 引用到子请求。

    我的大错误是认为我的注释是我的嵌套字段的本地,但它是一个全局聚合

    def mark_bet_scores(surveys_qs: models.QuerySet) -> models.QuerySet:
        """Annotate a bet queryset with the following fields:
        - maxpoints : the amount of possibles points to get on this bet (int)
        - score : the current survey score (from 0 to 100) (float)
        - points : the amount of points gained on this bet (int)
        """
        responses = AnswerResponse.objects \
            .filter(survey__pk=models.OuterRef('pk')) \
            .prefetch_related('answer', 'answer__question') \
            .annotate(
                realcoef=models.Case(
                    models.When(coef__isnull=True, then=models.F('answer__coef')),
                    models.When(coef__isnull=False, then=models.F('coef'))
                )
            ) \
            .annotate(
                points=models.ExpressionWrapper(
                    models.F('realcoef') * models.F('answer__question__points'),
                    output_field=models.IntegerField()
                )
            ) \
            .values('survey__pk') \
            .annotate(
                total=models.Sum(models.F('points'))
            ) \
            .values('total')
    
        # now we need to make the relation between `Survey.answerresponse` and
        # responses
        surveys = surveys_qs \
            .annotate(
                maxpoints=models.Sum('sections__question__points'),
                points=models.Subquery(
                    responses,
                    output_field=models.IntegerField()
                )
            ) \
            .annotate(
                score=models.Case(
                    models.When(maxpoints=0, then=0),
                    models.When(maxpoints__gt=0, then=models.ExpressionWrapper(
                        models.F('points') / models.F('maxpoints') * 100,
                        output_field=models.FloatField())
                    )
                )
            )
        return surveys
    
    

    【讨论】:

    猜你喜欢
    • 2014-07-27
    • 2013-06-10
    • 2016-02-06
    • 2011-11-27
    • 1970-01-01
    • 2015-03-15
    • 1970-01-01
    • 2012-05-04
    • 2013-04-17
    相关资源
    最近更新 更多