基于JSON在Django查询集上创建额外的列

时间:2019-02-22 03:09:02

标签: python json django django-admin django-queryset

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

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

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)

在这种情况下,它也可以说_item_namevalues_list上不是有效的列,即使使用了额外的内容也无法创建它。

  1. 尝试使用F表达式,效果不佳,因为在比较JSON时queryset有点混乱: queryset.annotate(_item_name=F('content__name'))

  2. 尝试使用RawSQL,但在我使用的场景(Django Admin)上无效。

总结,我需要以下一项才能使其起作用:

  • 一种合并具有不同值/值类型的两个查询集的方法
  • 一种注释F的方式,即使内容是JSON
  • 或者一种将RawQuerysets转换为普通查询集而又不丢失多余列的方法。

1 个答案:

答案 0 :(得分:0)

here一起记录了无法与JSONField一起使用的F对象,以及一种解决方法someone posted in the comments

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,我在JSONField中的字符串值上使用了Cast和不使用Cast时,都使用了它,似乎可行如预期的那样。不确定作者为什么决定编写KeyTextTransformFactory类,因为您可以直接调用KeyTextTransform(name, rhs)