6

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

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'

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

4

1 回答 1

12

您可以通过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'))

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

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

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

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

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

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

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

于 2019-03-09T10:01:04.303 回答