Django 使用 Postgres 在 JSONField 中注释计数

Django annotate count in JSONField with Postgres

使用 Django 我有一个 JSONField 类型的字段。我想对 json 中的嵌套 key/value 进行不同计数。使用普通字段,您可以执行以下操作

model.objects.values('field_name')\
.annotate(total=Count('field_name')).order_by('-total')

这不适用于 JSONField。

示例模型

class Pet(models.Model):
    data = JSONField()

数据示例

  {
    'name':'sparky',
    'animal':'dog',
    'diet':{
        'breakfast':'biscuits',
        'dinner':'meat',
    }
}

正在尝试

Pet.objects.values('data__diet__dinner')\
.annotate(total=Count('data__diet__dinner')).order_by('-total')

异常

TypeError: unhashable type: 'list'

执行此操作的正确方法是什么?

您可以使用 jsonb_extract_path_text via a Func 对象作为字段转换的替代方法:

Pet.annotate(dinner=Func(
    F('data'), Value('diet'), Value('dinner'),
    function='jsonb_extract_path_text'))  \
.values('dinner')  \
.annotate(total=Count('dinner'))

字段转换 data__diet__dinner 失败的原因是 Django 中的一个错误,当您深入 json 结构 使用GROUP BY 在 SQL。第一级(nameanimaldiet)应该可以正常工作。

原因似乎是对于嵌套转换,Django 更改了使用的 SQL 语法,从单个值切换为列表以指定进入 json 结构的路径。

这是用于非嵌套 json 转换(= 第一级)的语法:

"appname_pet"."data" -> 'diet'

这是用于嵌套转换(比第一层更深)的语法:

"appname_pet"."data" #> ARRAY['diet', 'dinner']

在构造查询时,Django 在计算所需的 GROUP BY 子句时卡在该列表上。这似乎不是不可避免的限制;对转换的支持是相当新的,这可能是尚未解决的问题之一。因此,如果您打开 Django ticket,这可能只适用于几个版本。