Как вычислить сумму агрегата с Django? - PullRequest
0 голосов
/ 30 января 2020

Я сейчас пытаюсь вычислить score из Survey в SQL только для того, чтобы иметь возможность заказать опрос по их оценкам,

мой текущий лог c: 1005 *

  • Вычислите действительный коэффициент моего Answer
  • Сделайте сумму этого коэффициента для моего Question (который может иметь кратные Answer, поэтому я использую сумму)
  • вычислить сумму points моего целого Survey на основе суммы всех Question баллов (Question.point * sum (Answer.coef)) basicaly
    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')
            )

схема базы данных выглядит примерно так: Опрос> Разделы> Вопросы (баллы)> Ответ (coef)> AnswerResponse (переопределение coef)

, и я получаю следующую ошибку:

FieldError: Cannot compute Sum('<CombinedExpression: F(answerresponse__realcoef_sum) *
F(answerresponse__answer__question__points)>'): '<CombinedExpression: F(answerresponse__realcoef_sum) *
F(answerresponse__answer__question__points)>' is an aggregate

, которая понимается как «эта sql часть еще не была выполнена, поэтому вы не можете на нее положиться»

возможно ли достичь этого, оставив только сторону SQL?

Ответы [ 2 ]

0 голосов
/ 31 января 2020

Мне удалось найти решение для этого: я использую подзапрос для вычисления итогового значения за опрос, затем аннотирую в наборе запросов Survey, метод OuterRef позволил мне получить ссылку на родительский запрос pk в subrequest.

Моя большая ошибка заключалась в том, что мои аннотации были локальными для моего вложенного поля, но это была глобальная агрегация

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

0 голосов
/ 30 января 2020

Вы можете сначала попытаться аннотировать результат answerresponse__realcoef_sum * answerresponse__answer__question__points с помощью оболочки выражений :

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

, а затем агрегировать результат по SUM:

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