Django通过谓词和计数结果注释查询集

Posted

技术标签:

【中文标题】Django通过谓词和计数结果注释查询集【英文标题】:Django annotate queryset by predicate and count results 【发布时间】:2022-01-10 05:18:51 【问题描述】:

我有两个模型:

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() 会导致性能不佳吗?

【问题讨论】:

【参考方案1】:

您可以使用Value 进行注释:

from django.db.models import Value

cnt = queryset.filter(score__is_rusk=True).count()
queryset = queryset.annotate(cnt=Value(cnt))

但这将添加 same 值:Games 中的Score 对象的数量queryset所有 Game 对象,这没有多大意义。

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

from django.db.models import Q, Sum

queryset.annotate(
    rusk_cnt=Sum('score', filter=Q(score__is_rusk=True))
)

【讨论】:

【参考方案2】:

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

Difference between Django's annotate and aggregate methods?

【讨论】:

以上是关于Django通过谓词和计数结果注释查询集的主要内容,如果未能解决你的问题,请参考以下文章

Django:通过注释字段的总和订购查询集?

Django 多注解返回错误结果

注释不同的 Django 查询集不再使用不同的查询集

如何在带注释的 Django 查询集结果上列出字段值?

Django:结合来自两个过滤器查询的两个计数注释

可以通过查询结果获取结果集行数吗?