python - 使用具有多个字段的值聚合来注释查询集

标签 python django postgresql django-models django-queryset

Django 注释真的很棒。但是,我不知道如何处理需要多个 values() 的注释。

问题:

我想用相关 m2m 中的项目计数来注释 author_queryset。但是,我不知道是否需要使用子查询:

annotated_queryset = author_queryset.annotate(genre_counts=Subquery(genre_counts))

返回:

SyntaxError: subquery must return only one column

我尝试将值转换为 JSONField 以将其返回到一个字段中,希望我可以使用 JSONBagg因为我使用的是 postgres 并且需要过滤结果:

subquery = Author.objects.filter(id=OuterRef('pk')).values('id','main_books__genre').annotate(genre_counts=Count('main_books__genre'))

qss = qs.annotate(genre_counts=Subquery(Cast(subquery,JSONField()), output_field=JSONField()))

产量:

AttributeError: 'Cast' object has no attribute 'clone'

我不确定需要什么才能将 dict 转换为 JSONField()。有一些很棒的info here关于过滤这些。开发版本中还有一些针对 postgres 的功能即将推出,名为 ArraySubQuery()看起来可以解决这个问题。但是,在稳定版本发布之前我无法使用此功能。

期望的结果

我想进行注释,以便我可以根据注释进行过滤,如下所示:

annotated_queryset.filter(genre_counts__scifi__gte=5)

详细信息

我可以使用 dunders 获取相关字段,然后像这样计数:

# get all the authors with Virginia in their name
author_queryset = Author.objects.filter(name__icontains='Virginia')
author_queryset.count()
# returns: 21

# aggregate the book counts by genre in the Book m2m model
genre_counts = author_queryset.values('id','main_books__genre').annotate(genre_counts=Count('main_books__genre'))
genre_counts.count()

# returns: 25

这是因为查询集中的每个 Author 对象可能返回多个流派计数。在这个特定的示例中,有一位作者拥有 4 种不同类型的书籍:

举例说明:

...
{'id': 'authorid:0054f04', 'main_books__genre': 'scifi', 'genre_counts': 1}
{'id': 'authorid:c245457', 'main_books__genre': 'fantasy', 'genre_counts': 4}
{'id': 'authorid:a129a73', 'main_books__genre': None, 'genre_counts': 0}
{'id': 'authorid:f41f14b', 'main_books__genre': 'mystery', 'genre_counts': 16}
{'id': 'authorid:f41f14b', 'main_books__genre': 'romance', 'genre_counts': 1}
{'id': 'authorid:f41f14b', 'main_books__genre': 'scifi', 'genre_counts': 9}
{'id': 'authorid:f41f14b', 'main_books__genre': 'fantasy', 'genre_counts': 3}
...

还有另一位作者有 2 个,其余的各有一个流派。这是 25 个值。

希望这对某人有意义!我确信有一种方法可以正确处理这个问题,而无需等待上述功能。

最佳答案

您想要使用不带 子查询.annotate( ,因为正如您所发现的,它需要返回单个值。您应该能够跨越第一个注释的计数表达式。

不幸的是,Django 目前不支持您正在寻找的 genre_counts__scifi_gt=5 内容。您可以构造它,以便使用传递给它的过滤器进行计数。

selected_genre = 'scifi'
annotated_queryset = author_queryset.annotate(
    genre_count=Count("main_books__genre", filter=Q(genre=selected_genre))
).filter(genre_count__gte=5)

要获得完整的分割,您最好返回分割并在应用程序中进行最终聚合,如您在问题中所示。

关于python - 使用具有多个字段的值聚合来注释查询集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69075483/

相关文章:

python - 在虚拟环境中安装python站 pip 包

python - python 2.7 urllib2 和 json 中的脚本引发 unicode 错误

python - 使文件可下载

Django:如何从模板中包含 View

python - 为什么我的数据框列发生了变化?

python - mongoengine 操作的原子性如何

python - 测试 Django throttle

postgresql - 将 Power BI 服务连接到 Azure 上的 PostgreSQL 数据库

ruby-on-rails - Postgres 时区应该从 UTC 更改吗?

postgresql - 存在时的情况 - 列 (SQL)