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

Posted

技术标签:

【中文标题】Postgres:使用 django 对 json 键进行值查询【英文标题】:Postgres: values query on json key with django 【发布时间】:2017-07-29 06:50:03 【问题描述】:

我需要在 django 1.10 中对 postgres 支持的 jsonfield 上的嵌套键执行 values/values_list 查询 例如。

class AbcModel(models.model):
    context = fields.JSONField()

如果它有如下值:


  'lev1': 
    'lev': 2
  

我想运行类似的查询

AbcModel.objects.values('context__lev1__lev2').distinct()
AbcModel.objects.values_list('context__lev1__lev2', flat=True).distinct()

编辑: JSON 字段是来自 django.contrib.postgres.fields 的官方 django JSONField

【问题讨论】:

那么您面临的问题是什么? django 返回错误 FieldError: Cannot resolve keyword 'lev1' into field。不允许加入“上下文”。 这是您数据库中的准确 json 表示吗? 什么意思? Postgres 数据类型是 Jsonb。结构相同的是数据库和这里显示的内容 【参考方案1】:

所以我找到了一个解决方案,这适用于 django 1.10 及更高版本。 我使用 KeyTransform 来注释和提取下一个键,并在其上做了一个 values_list。

from django.contrib.postgres.fields.jsonb import KeyTransform
extracted_query = AbcModel.objects.annotate(lev1=KeyTransform('lev1', 'context')).annotate(lev2=KeyTransform('lev', 'lev1'))

这个查询允许我使用 lev1 和 lev2 作为模型中的普通字段,所以我可以对字段执行值、values_list 或任何其他有效查询。

Django 1.11 允许将两个 Transforms 嵌套在一个注释中,不确定 1.10 关于嵌套,因为我已升级到 1.11

【讨论】:

KeyTransform 被认为是 Django 中非公共 api 的一部分吗?从 Django 2 开始,似乎在文档中的任何地方都没有提到它。 它可以被视为一个非公共 api,因为它没有在任何地方记录,但同时,我发现 postgres 特定的东西不像 django 的其他部分那样有很好的记录,但它不太可能要更改,因为它会影响太多事情,请查看此错误报告,提出相同的观点code.djangoproject.com/ticket/29482 @Shaumux 如果它是动态的,我们如何评估lev1 键?意思是lev 是像OuterRef 一样从Subquery 传递的密钥 @NwawelAIroume 我不确定您所说的动态到底是什么意思,键是动态的吗?您应该仍然可以使用 Cast 函数转换为 JSON 类型并使用相同的方法,但可能会有很多嵌套调用 @Shaumaux 我注意到Cast 有助于提取整个字典。但我只想提取字典键的值。我想做类似annotate(lev1=KeyTransform(OuterRef("external_model_column_value"), 'context'))【参考方案2】:

这并不理想,但我能够通过将 json 字段添加为额外字段然后在该额外字段上调用值来实现此功能:

AbcModel.objects.extra(select=
    "extra_field": "context->'lev1'->'lev2'"
).values('extra_field').distinct()
AbcModel.objects.extra(select=
    "extra_field": "context->'lev1'->'lev2'"
).values_list('extra_field', flat=True).distinct()

【讨论】:

如何让-> 运算符工作?我想用它而不是很多KeyTransform,但是得到这个错误:django.db.utils.ProgrammingError: operator is not unique: unknown -> unknown LINE 1: SELECT ('step_data'->'step_data'->'workshop') AS "w" FROM "w... ^ HINT: Could not choose a best candidate operator. You might need to add explicit type casts. @dirkgroten KeyTransform 将使用 -> 运算符,如果您使用像 'step_data' -> 'lev1' 这样的单级提取,如果您使用嵌套或链式注释来提取,它将使用 #> 运算符嵌套数据,例如。 'step_data' -> 'lev 1' -> 'lev2' 是的,我明白了,感谢您的回复。但是我遇到的问题是使用上面显示的显式 select 命令时。使用-> 运算符进行查询会显式抛出我显示的错误(operator is not unique)。

以上是关于Postgres:使用 django 对 json 键进行值查询的主要内容,如果未能解决你的问题,请参考以下文章

使用 Django 从 Postgres 导出 JSON 时结果不一致

使用 postgres 在 Django 中高效存储 Json

Django + Postgres:将 JSON 字符串作为 JSON 类型直接保存到模型中

在 postgres,Django 中保存大型 json 对象

在 django 的测试中找不到 Postgres 函数 json_array_elements

在 django 的 postgres db 中将 json 保存为数组