首页 > 解决方案 > 基于 JSON 在 Django 查询集上创建一个额外的列

问题描述

这是一个非常具体的问题。

我的模型有一个名为“内容”的 JSON,在该 JSON 中有一个名为“名称”的键。我的目标是将名称放入查询集的新列中,但这似乎很难做到,因为content__name, 在某些情况下不存在,如果您只是使用 extra 选择它,它将引发异常。

queryset = queryset.extra(select={'_content_name': "SELECT content->>'name'"})

最终结果需要包含包含名称的和不包含名称的,不包含的可以替换为“-”等字符或完全为空。 最终结果需要是查询集,而不是 RawQueryset。

我尝试过但不太奏效的事情:

  1. 之前使用过滤并尝试与原始的差异进行联合,由于查询集具有不同的列数量或列具有不同的类型,无法联合。您不能将 JSON 合并到与字符串content__name相同的列中。content->>'name'
qs = queryset.filter(~Q(content__name__iexact='')).values_list('content__name')
qs2 = queryset.difference(qs).extra(select={'_item_name': "SELECT content->>'name'"}).values_list('_item_name')
queryset = qs.union(qs2)

在这种情况下,即使在使用 extra 创建它之后,它也可以说它_item_name不是一个有效的列。values_list

  1. 尝试使用 F 表达式,但不太奏效,因为查询集在比较 JSON 时有点困惑: queryset.annotate(_item_name=F('content__name'))

  2. 尝试使用 RawSQL,但它不适用于我正在使用的场景(Django Admin)。

总结一下,我需要这些项目之一,它可能会起作用:

标签: pythonjsondjangodjango-admindjango-queryset

解决方案


此处记录了不与 JSONField 一起使用的 F 对象,以及评论中发布的解决方法:

class KeyTextTransformFactory:

    def __init__(self, key_name):
        self.key_name = key_name

    def __call__(self, *args, **kwargs):
        return KeyTextTransform(self.key_name, *args, **kwargs)

class JSONF(F):

    def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
        rhs = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)

        field_list = self.name.split(LOOKUP_SEP)
        for name in field_list[1:]:
            rhs = KeyTextTransformFactory(name)(rhs)
        return rhs

rhs中必须包含Cast,

Sample.objects.filter(jsonfield__lookup__value=Cast(JSONF('value'), 
    IntegerField()))

上面引用的帖子中缺少的导入是:

from django.db.models.functions import Cast
from django.db.models import CharField
from django.contrib.postgres.fields.jsonb import KeyTextTransform 

LOOKUP_SEP变量应该是'__'.

话虽如此,我已经尝试过了,它适用于您的注释案例。此外,Cast除非您期望的类型不是 ,否则可能没有必要str,我已经在 J​​SONField 中的字符串值上使用和不使用 Cast 并且它似乎按预期工作。不知道为什么作者决定写这KeyTextTransformFactory门课,因为你可以直接打电话KeyTextTransform(name, rhs)


推荐阅读