使用 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'",()))
我说“捏造”是因为它似乎是对符号的滥用,需要对整数索引进行特殊处理。
仍然希望得到更好的答案。
我可以建议使用更简洁的方法:
django 的 Func
https://docs.djangoproject.com/en/2.0/ref/models/expressions/#func-expressions
和 postgres 函数 jsonb_extract_path_text
https://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'
),
)
也许更好的解决方案(对于 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
我将 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'",()))
我说“捏造”是因为它似乎是对符号的滥用,需要对整数索引进行特殊处理。
仍然希望得到更好的答案。
我可以建议使用更简洁的方法:
django 的
Func
https://docs.djangoproject.com/en/2.0/ref/models/expressions/#func-expressions和 postgres 函数
jsonb_extract_path_text
https://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' ), )
也许更好的解决方案(对于 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