Django使用Postgres在JSONField中注释计数

ms1*_*s18 4 django postgresql django-jsonfield django-annotate

使用Django,我有一个JSONField类型的字段。我想对json中的嵌套键/值进行不同的计数。在普通场中,您可以像下面这样进行操作

model.objects.values('field_name')\
.annotate(total=Count('field_name')).order_by('-total')
Run Code Online (Sandbox Code Playgroud)

这不适用于JSONField。

示例模型

class Pet(models.Model):
    data = JSONField()
Run Code Online (Sandbox Code Playgroud)

数据示例

  {
    'name':'sparky',
    'animal':'dog',
    'diet':{
        'breakfast':'biscuits',
        'dinner':'meat',
    }
}
Run Code Online (Sandbox Code Playgroud)

Pet.objects.values('data__diet__dinner')\
.annotate(total=Count('data__diet__dinner')).order_by('-total')
Run Code Online (Sandbox Code Playgroud)

的例外

TypeError: unhashable type: 'list'
Run Code Online (Sandbox Code Playgroud)

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

End*_*oth 5

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

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

字段转换data__diet__dinner失败的原因是,当您深入到json结构中的一层GROUP BY在SQL中使用时,Django中出现错误。第一级(nameanimaldiet)应该工作的罚款。

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

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

"appname_pet"."data" -> 'diet'
Run Code Online (Sandbox Code Playgroud)

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

"appname_pet"."data" #> ARRAY['diet', 'dinner']
Run Code Online (Sandbox Code Playgroud)

在构造查询时,Django在制定所需GROUP BY子句时会在该列表上阻塞。这似乎不是必然的限制;对转换的支持是非常新的,这可能是尚未解决的问题之一。因此,如果您打开Django票证,则可能只适用于一些版本。