Django аннотирует набор запросов по предикатам и подсчитывает результаты
У меня есть две модели:
class Game(models.Model):
id = models.AutoField(primary_key=True)
class Score(models.Model):
id = models.AutoField(primary_key=True)
game = models.ForeignKey(Game, related_name="score", on_delete=models.CASCADE)
first_score = models.IntegerField(blank=True)
second_score = models.IntegerField(blank=True)
is_rusk = models.BooleanField(blank=True)
И я получил набор объектов Game:
[
{
"id": 314317035,
"score": [
{
"first_score": 5,
"second_score": 1,
"is_rusk": false
}
]
},
{
"id": 311298177,
"score": [
{
"first_score": 5,
"second_score": 2,
"is_rusk": false
}
]
},
{
"id": 310278749,
"score": [
{
"first_score": 5,
"second_score": 2,
"is_rusk": false
}
]
},
{
"id": 309866238,
"score": [
{
"first_score": 5,
"second_score": 0,
"is_rusk": true
}
]
},
{
"id": 307926664,
"score": [
{
"first_score": 5,
"second_score": 0,
"is_rusk": true
}
]
},
{
"id": 306047964,
"score": [
{
"first_score": 4,
"second_score": 5,
"is_rusk": false
}
]
},
{
"id": 304881611,
"score": [
{
"first_score": 5,
"second_score": 3,
"is_rusk": false
}
]
},
{
"id": 304468136,
"score": [
{
"first_score": 5,
"second_score": 2,
"is_rusk": false
}
]
},
]
Я хочу аннотировать этот кверисет с rusks_cnt
, это будет подсчет объектов с is_rusk=True
, Если есть способ не добавлять это к каждому объекту, просто как одно поле, это тоже было бы хорошо.
Я думаю, что проще всего сделать это следующим образом:
cnt = queryset.filter(score__is_rusk=True).count()
Но когда я пытаюсь сделать аннотацию следующим образом:
cnt = queryset.filter(score__is_rusk=True).count()
queryset = queryset.annotate(cnt=cnt)
В нем говорится:
QuerySet.annotate() received non-expression(s): 2.
Я также пробовал:
queryset = queryset.annotate(
rusk_cnt=Sum(
Case(When(score__is_rusk=True, then=1)), output_field=IntegerField()
)
)
Но результаты таковы:
[
{
"id": 279658929,
"rusk_cnt": 1
},
{
"id": 279796553,
"rusk_cnt": null
},
...
]
Также мне интересно, приведет ли простое использование .count()
к плохой производительности?
Вы можете аннотировать с помощью Value
:
from django.db.models import Value
cnt = queryset.filter(score__is_rusk=True).count()
queryset = queryset.annotate(cnt=Value(cnt))
Но это добавит одно и то же значение: количество Score
объектов для Game
в queryset
к всех Game
объектов, что не имеет особого смысла.
Если вы хотите аннотировать Game
объекты с True
количеством объектов, где Score
с is_rusk=True
, вы можете работать с:
from django.db.models import Q, Sum
queryset.annotate(
rusk_cnt=Sum('score', filter=Q(score__is_rusk=True))
)
Annotate предназначен для вычисления по каждой записи. Если вы хотите рассчитать для всего набора запросов, используйте Aggregate.