Объединение должно принимать как минимум два выражения - PullRequest
0 голосов
/ 04 мая 2019

Я использую Django и Python 3.7. Я хочу написать выражение Coalesce, чтобы помочь мне написать запрос Django большего размера. У меня

            Coalesce(
                F("votes")
                -
                Subquery(relevant_hour_stats.values('votes_threshold')[:1]),
                output_field=models.FloatField())

Вот выражение в контексте ...

qset = (
    ArticleStat.objects
        .all()
        .annotate(
        shifted_article_create_hour=ExtractHour(ExpressionWrapper(
            F('article__created_on')
            +
            timedelta(seconds=avg_fp_time_in_seconds),
            output_field=models.DateTimeField()
        ))
    )
        .annotate(
        votes_above_threshold=(
                Coalesce(
                    F("votes")
                    -
                    Subquery(relevant_hour_stats.values('votes_threshold')[:1]),
                    output_field=models.FloatField())
        ),
    )
        .filter(
        votes_above_threshold__gt=0,
    )
)

но это приводит к

Coalesce must take at least two expressions

жалуется на линию

output_field=models.FloatField()

Насколько я могу судить, у меня есть два выражения. На что еще может ссылаться ошибка?

1 Ответ

0 голосов
/ 04 мая 2019

«Выражение» в терминах джанго - это примеры django.db.models.expressions.Expression.

F('votes') - выражение.

Subquery(...) - выражение тоже.

Но expression+expression == combined_expression, поэтому F(...) - Subquery(...) - это одно "сложное" выражение.

Вам нужен второй POSITIONAL аргумент для Coalesce:

Coalesce(
    (F(...) - Subquery(relevant_hour_stats.values('votes_threshold')[:1])),  # First expression
    Value(0.0),  # Second expression
    output_field=models.FloatField()
)

И я думаю, что подзапрос может потенциально привести к NULL (не F()), поэтому лучше обернуть только Подзапрос в Coalesce:

qset = (
    ArticleStat.objects
        .all()
        .annotate(
            shifted_article_create_hour=ExtractHour(
                ExpressionWrapper(
                    F('article__created_on') + timedelta(seconds=avg_fp_time_in_seconds),
                    output_field=models.DateTimeField()
                )
            ),
        )
        .annotate(
            votes_above_threshold=(  
                # Single expression F()-Coalesce(...) wrapped in parenthesis
                F("votes") - Coalesce(
                    # 2 expressions as arguments to Coalesce:
                    Subquery(relevant_hour_stats.values('votes_threshold')[:1]),
                    Value(0.0),
                    output_field=models.FloatField(),
                )
            ),
        )
        .filter(
            votes_above_threshold__gt=0,
        )
)
...