使用 django 查询 set values() 索引到 JSONField

Posted

技术标签:

【中文标题】使用 django 查询 set values() 索引到 JSONField【英文标题】:Using django query set values() to index into JSONField 【发布时间】:2016-12-24 18:25:53 【问题描述】:

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

示例:

给定一个带有options JSONField 的Car 模型,我想做的类似于

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

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

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

编辑 - 一种解决方法:

玩过之后,看起来可以通过在上面的两行代码之间插入以下内容来伪造:

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

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

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

【问题讨论】:

【参考方案1】:

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

django 的Func https://docs.djangoproject.com/en/2.0/ref/models/expressions/#func-expressions

和postgres函数jsonb_extract_path_texthttps://www.postgresql.org/docs/9.5/static/functions-json.html

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'
    ), 
)

【讨论】:

谢谢!这确实更简洁一些(避免调用RawSQL),尽管它仍然使用我的符号滥用(用双下划线注释)并且在功能上几乎相同(如果我使用#> 运算符而不是-> 在原始 sql 中),对吧?不知道多索引的东西是不是效率更高?【参考方案2】:

也许更好的解决方案(对于 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 表达式以提取您需要的值。

【讨论】:

哇,有一段时间了。我的问题可能应该更明确一点:我正在寻找一种将字符串映射到操作的通用方法,以便我可以将此操作与一整套其他 values 调用一起合并。不过,我没有看到KeyTextTransform,这似乎是处理查找的正确方法。感谢您指出这一点!【参考方案3】:
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

【讨论】:

以上是关于使用 django 查询 set values() 索引到 JSONField的主要内容,如果未能解决你的问题,请参考以下文章

django 查询集 select_related 。 values() 重命名键

在 Django 中注释 values() 查询

Postgres:使用 django 对 json 键进行值查询

django如何跨表查询

如何使用 Django 中另一个查询集的结果过滤查询集?

django: filter.values(...) 不检索请求的字段