【问题标题】:Create an extra column on a Django queryset based on a JSON基于 JSON 在 Django 查询集上创建一个额外的列
【发布时间】:2019-07-16 02:24:23
【问题描述】:

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

我的模型有一个名为“内容”的 JSON,在该 JSON 中有一个名为“名称”的键。我的目标是将名称放入查询集的新列中,但这似乎很难做到,因为 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_name 不是 values_list 上的有效列,即使在使用 extra 创建它之后也是如此。

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

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

总结一下,我需要以下其中一项才能正常工作:

  • 联合两个具有不同值/值类型的查询集的方法
  • 即使内容是 JSON 也可以注释 F 的方法
  • 或者一种将 RawQuerysets 转换为普通查询集而不丢失额外列的方法。

【问题讨论】:

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


    【解决方案1】:

    无法使用 JSONField 的 F 对象已记录在 here,以及解决方法 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,我已经在 J​​SONField 中的字符串值上使用和不使用 Cast 并且它似乎按预期工作。不知道为什么作者决定编写KeyTextTransformFactory 类,因为您可以直接调用KeyTextTransform(name, rhs)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-30
      • 2023-03-22
      • 1970-01-01
      • 2015-01-05
      • 2017-03-09
      • 1970-01-01
      • 2018-12-28
      • 2014-12-14
      相关资源
      最近更新 更多