Django通过谓词和计数结果来注释queryset



我有两种型号:

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)

我得到了一个游戏对象的查询集:

[
{
"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()会导致糟糕的性能吗?

注释用于计算每个条目。如果要计算整个查询集,请使用"聚合"。

Django';s注释和聚合方法?

您可以使用Value:进行注释

from django.db.models importValue
cnt = queryset.filter(score__is_rusk=True).count()
queryset = queryset.annotate(cnt=Value(cnt))

但这将添加相同的值:querysetGameScore对象数到所有Game对象数,这没有多大意义。

如果您想用True注释Game对象——其中Scoreis_rusk=True的对象数量,您可以使用:

from django.db.models importQ, Sum
queryset.annotate(
rusk_cnt=Sum('score', filter=Q(score__is_rusk=True))
)

最新更新