1

这是一个非常复杂的问题,所以让我解释一下。我有一个名为 Person 的模型,它将大部分数据存储在 JSONField 中。

class Person(models.Model):
    data = JSONField()

现在,数据字段通常采用以下格式:

{"name" : <String>, "age" : <int>}

现在,我要做的是创建一个 Person 的查询集,它使用age其字段中的属性data按降序对对象进行排序。这是使用以下代码解决的:

from django.db.models.expressions import RawSQL
from .models import Person

qs = Person.objects.annotate(age=RawSQL("(data->>'age')::int", [])).order_by('-age')

这很棒,而且效果很好。但是,在测试期间,我将data一个 Person 对象的属性更改为如下所示:

{"name" : <String>, "profession" : <String>} 

age也就是说,该对象在其data字段中没有属性。现在,当我运行上面的查询时,它仍然可以正常工作,但是这个对象(没有age属性的对象)位于最顶部。这是由于以下两个原因之一:

  • 由于其age为空,因此由于降序 order_by 函数将其发送到顶部。
  • 它是我上次创建的对象,所以它总是在开头,但是因为它没有age属性,所以它根本不受 order_by 函数的影响,它保持在原来的位置。

我真正想做的是将所有age在其字段中没有属性的对象发送data到查询集的最后。

我通过创建 2 个查询集(一个年龄不为空,一个为空)尝试了联合方法,并使用|运算符将​​它们连接起来。这不起作用,因为订购搞砸了。我还尝试了在另一个问题中发现的这种奇怪的方法(也没有用):

qs = Person.objects.annotate(age=RawSQL("(data->>'age')::int", [])).extra(select={'is_top': "age__isnull=True"})
qs = qs.extra(order_by('-is_top')

链接到不起作用的奇怪解决方案

无论如何,有没有不涉及列表、itertools 和链的方法来做到这一点?因为我听说它们有时会很慢。

谢谢!

注意:请不要回答有关为这些查询规范化数据库而不是使用 JSONFields 的问题。我很清楚规范化的好处,但对于我的用例,它必须是 JSONField。

4

1 回答 1

3

如果密钥查找失败,结果将NULLPostgreSQL 文档中指定:

注意:对于 json 和 jsonb 类型,这些运算符都有并行变体。字段/元素/路径提取运算符返回与其左侧输入相同的类型(json 或 jsonb),但指定为返回文本的那些除外,它将值强制为文本。如果 JSON 输入没有正确的结构来匹配请求,则字段/元素/路径提取运算符返回NULL,而不是失败;例如,如果不存在这样的元素。

可以这样做,您只需使用.desc(nulls_last=True)[Django-doc]订购:

from django.db.models import F, RawSQL
from .models import Person

qs = Person.objects.annotate(
    age=RawSQL("(data->>'age')::int", [])
).order_by(F('age').desc(nulls_last=True))

这将通过以下方式对元素进行排序:

-- SQL query
ORDER BY age IS NULL, age DESC

因此,通过首先排序age IS NULL,这将导致TRUE,因此,它被排序在结果表的底部

于 2018-10-09T17:05:32.673 回答