这是一个非常具体的问题。
我的模型有一个名为“ content”的JSON,在该JSON中有一个名为“ name”的键。我的目标是将名称放入查询集的新列中,但这似乎很难做到,因为在某些情况下 content__name
可能不存在,如果您仅使用多余的名称进行选择,将引发异常。
queryset = queryset.extra(select={'_content_name': "SELECT content->>'name'"})
最终结果需要同时包含包含名称的名称和不包含名称的名称,不包含名称的名称可以替换为“-”或完全为空。 最终结果必须是查询集,而不是RawQueryset。
我尝试过但没有奏效的事情:
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
上不是有效的列,即使使用了额外的内容也无法创建它。
尝试使用F表达式,效果不佳,因为在比较JSON时queryset有点混乱:
queryset.annotate(_item_name=F('content__name'))
尝试使用RawSQL,但在我使用的场景(Django Admin)上无效。
总结,我需要以下一项才能使其起作用:
答案 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)
。