使用 django query set values() 索引到 JSONField

Using django query set values() to index into JSONField

我将 django 与 postgres 一起使用,并且在我的模型中有一堆 JSON 字段(其中一些非常大和详细)。我正在从基于 char 的字段切换到 jsonb 字段,这允许我在字段内的键上 filter,我想知道是否有任何方法可以从调用查询集 values 方法。

示例:

给定一个带有 options JSON 字段的 Car 模型,我想做的是

qset = Car.objects.filter(options__interior__color='red')
vals = qset.values('options__interior__material')

请原谅蹩脚的玩具问题,但希望它能传达这个想法。这里 filter 调用完全符合我的要求,但是对 values 的调用似乎没有意识到 JSON 字段的特殊性。我收到错误消息,因为 values 找不到要加入的名为“interior”的字段。是否有其他一些我缺少的语法或选项可以使这项工作正常进行?

似乎是对现有功能的一个非常明显的扩展,但到目前为止,我未能在文档中或通过堆栈溢出或 google 搜索找到任何对类似内容的引用。

编辑 - 解决方法:

经过尝试,看起来可以通过在上面两行代码之间插入以下内容来捏造:

qset=qset.annotate(options__interior__material=RawSQL("SELECT options->'interior'->'material'",()))

我说“捏造”是因为它似乎是对符号的滥用,需要对整数索引进行特殊处理。

仍然希望得到更好的答案。

我可以建议使用更简洁的方法:

from django.db.models import F, Func, CharField, Value
Car.objects.all().annotate(options__interior__material =
    Func(
        F('options'),
        Value('interior'),
        Value('material'),
        function='jsonb_extract_path_text'
    ), 
)

也许更好的解决方案(对于 Django >= 1.11)是使用这样的方法:

from django.contrib.postgres.fields.jsonb import KeyTextTransform

Car.objects.filter(options__interior__color='red').annotate(
    interior_material=KeyTextTransform('material', KeyTextTransform('interior', 'options'))
).values('interior_material')

请注意,您可以嵌套 KeyTextTransform 表达式来提取您需要的值。

Car.objects.extra(select={'interior_material': "options#>'{interior, material}'"})
.filter(options__interior__color='red')
.values('interior_material')

您可以利用 .extra() 并添加 postgres jsonb 运算符

Postgres jsonb 运算符:https://www.postgresql.org/docs/9.5/static/functions-json.html#FUNCTIONS-JSON-OP-TABLE