Django .aggregate()在.annotate()上

0qx6xfy6  于 2023-01-21  发布在  Go
关注(0)|答案(3)|浏览(146)

是否可以聚合查询集的注解?

型号:

class Article(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()

class State(models.Model):
    article = Models.ForeignKey(Article)
    date = DateField()
    views = IntegerField()
    downloads = IntegerField()

我正在努力做到以下几点:

articles = metrics_models.Article.objects.filter(
    state__date__month=month,
    state__date__year=year
).annotate(
    views=Min('state__views'),
    downloads=Min('state__downloads')
).aggregate(
    views=Sum('views'),
    downloads=Sum('downloads')
)

错误:

Exception Type: DatabaseError
Exception Value:    
column "downloads" does not exist
LINE 1: SELECT SUM(downloads), SUM(views) FROM (SELECT "metrics_arti...

当运行这个命令时,我得到了一个DatabaseError,因为django试图在'views'和'download'数据库列上进行聚合,而不是在注解上。
是否有其他方法可以在QuerySet注解上进行这种聚合?

mzaanser

mzaanser1#

我认为medmunds是正确的,你不能重复使用相同的名称来注解和聚合别名。我认为这就是你想要做的:

articles = metrics_models.Article.objects.filter(
    state__date__month=month,
    state__date__year=year
).annotate(
    min_views=Min('state__views'),
    min_downloads=Min('state__downloads')
).aggregate(
    sum_min_views=Sum('min_views'),
    sum_min_downloads=Sum('min_downloads')
)
8oomwypt

8oomwypt2#

根据你最初的询问,我认为这是可行的。

from django.db.models import ExpressionWrapper, Min, OuterRef

articles = (
    Article.objects.filter(state__date__month=month, state__date__year=year)
    .annotate(
        views=ExpressionWrapper(
            Min(
                State.objects.filter(article_id=OuterRef("id")).values_list(
                    "views", flat=True
                )
            ),
            output_field=models.IntegerField(),
        ),
        downloads=ExpressionWrapper(
            Min(
                State.objects.filter(article_id=OuterRef("id")).values_list(
                    "downloads", flat=True
                )
            ),
            output_field=models.IntegerField(),
        ),
    )
    .aggregate(views=Sum("views"), downloads=Sum("downloads"))
)
envsm3lx

envsm3lx3#

当您为annotate()方法和aggregate()方法使用相同的字段名时,您将在查询集中的每个对象上创建一个具有该名称的新字段,然后尝试获取该字段的总和。
但是,annotate()方法创建的新字段与传递给aggregate()方法的字段不同。
annotate()方法在包含计算结果的查询集中的每个对象上创建一个新字段,并且这个字段不存储在数据库中,它只存在于内存中的查询集中。
另一方面,传递给aggregate()方法的字段是对存储在数据库/queryset中的字段的引用,它用于获取存储在数据库中的值的总和。
因此,当您在annotate()和aggregate()方法中使用相同的字段名时,您试图对一个只存在于内存中的字段的值求和(您在aggregate方法中声明的字段覆盖了annotate中声明的字段),而不是数据库/查询集中的字段的值求和,这就是为什么您得不到预期结果的原因。

相关问题