使用 JSONField 的属性对 Django 查询集进行排序,前提是该属性并非随处可见
Ordering Django queryset using attributes of a JSONField, provided the attribute is not present everywhere
这是一个非常复杂的问题,所以让我解释一下。我有一个名为 Person 的模型,它将大部分数据存储在 JSONField 中。
class Person(models.Model):
data = JSONField()
现在,数据字段通常采用以下格式:
{"name" : <String>, "age" : <int>}
现在,我想要做的是创建一个 Person 的查询集,它使用 data
字段中的 age
属性对对象进行降序排序。这是使用以下代码解决的:
from django.db.models.expressions import RawSQL
from .models import Person
qs = Person.objects.annotate(age=RawSQL("(data->>'age')::int", [])).order_by('-age')
这很棒,而且效果很好。但是,在测试期间,我将一个 Person 对象的 data
属性更改为如下内容:
{"name" : <String>, "profession" : <String>}
也就是说,此对象在其 data
字段中没有 age
属性。现在,当我 运行 上面的查询时,它仍然可以正常工作,但是这个对象(没有 age
属性的对象)位于最顶部。这是由于以下两个原因之一:
- 由于它的
age
是空的,它被发送到顶部,因为下降 order_by 函数。
- 是我上次创建的对象,所以一直在开头,但是因为没有
age
属性,所以根本不受order_by函数的影响,而且它保持原来的位置。
我真正想做的是将所有在 data
字段中没有 age
属性的对象发送到查询集的最后。
我通过创建 2 个查询集(一个年龄不为空,一个年龄为空)尝试了联合方法,并使用 |
运算符加入它们。这没有用,因为顺序搞砸了。我也尝试了我在另一个问题中发现的这种奇怪的方法(也没有用):
qs = Person.objects.annotate(age=RawSQL("(data->>'age')::int", [])).extra(select={'is_top': "age__isnull=True"})
qs = qs.extra(order_by('-is_top')
Link to the weird solution that did not work
不管怎样,有没有不涉及列表、itertools 和链的方法?因为我听说他们有时会很慢。
谢谢!
注意:请不要回答有关为这些查询规范化数据库而不是使用 JSONFields 的问题。我很清楚规范化的好处,但对于我的用例,它必须是 JSONField。
如果键查找失败,结果为 NULL
,如 PostgreSQL documentation:
中指定
Note: There are parallel variants of these operators for both the json and jsonb types. The field/element/path extraction operators return the same type as their left-hand input (either json or jsonb), except for those specified as returning text, which coerce the value to text. The field/element/path extraction operators return NULL
, rather than failing, if the JSON input does not have the right structure to match the request; for example if no such element exists.
你可以做到这一点,你只需用.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')<b>.desc(nulls_last=True)</b>)
这将按以下顺序排列元素:
-- SQL query
ORDER BY <b>age IS NULL</b>, age DESC
因此,首先按 age IS NULL
排序,这将导致 TRUE
,结果,它被排序在结果的 底部 table.
这是一个非常复杂的问题,所以让我解释一下。我有一个名为 Person 的模型,它将大部分数据存储在 JSONField 中。
class Person(models.Model):
data = JSONField()
现在,数据字段通常采用以下格式:
{"name" : <String>, "age" : <int>}
现在,我想要做的是创建一个 Person 的查询集,它使用 data
字段中的 age
属性对对象进行降序排序。这是使用以下代码解决的:
from django.db.models.expressions import RawSQL
from .models import Person
qs = Person.objects.annotate(age=RawSQL("(data->>'age')::int", [])).order_by('-age')
这很棒,而且效果很好。但是,在测试期间,我将一个 Person 对象的 data
属性更改为如下内容:
{"name" : <String>, "profession" : <String>}
也就是说,此对象在其 data
字段中没有 age
属性。现在,当我 运行 上面的查询时,它仍然可以正常工作,但是这个对象(没有 age
属性的对象)位于最顶部。这是由于以下两个原因之一:
- 由于它的
age
是空的,它被发送到顶部,因为下降 order_by 函数。 - 是我上次创建的对象,所以一直在开头,但是因为没有
age
属性,所以根本不受order_by函数的影响,而且它保持原来的位置。
我真正想做的是将所有在 data
字段中没有 age
属性的对象发送到查询集的最后。
我通过创建 2 个查询集(一个年龄不为空,一个年龄为空)尝试了联合方法,并使用 |
运算符加入它们。这没有用,因为顺序搞砸了。我也尝试了我在另一个问题中发现的这种奇怪的方法(也没有用):
qs = Person.objects.annotate(age=RawSQL("(data->>'age')::int", [])).extra(select={'is_top': "age__isnull=True"})
qs = qs.extra(order_by('-is_top')
Link to the weird solution that did not work
不管怎样,有没有不涉及列表、itertools 和链的方法?因为我听说他们有时会很慢。
谢谢!
注意:请不要回答有关为这些查询规范化数据库而不是使用 JSONFields 的问题。我很清楚规范化的好处,但对于我的用例,它必须是 JSONField。
如果键查找失败,结果为 NULL
,如 PostgreSQL documentation:
Note: There are parallel variants of these operators for both the json and jsonb types. The field/element/path extraction operators return the same type as their left-hand input (either json or jsonb), except for those specified as returning text, which coerce the value to text. The field/element/path extraction operators return
NULL
, rather than failing, if the JSON input does not have the right structure to match the request; for example if no such element exists.
你可以做到这一点,你只需用.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')<b>.desc(nulls_last=True)</b>)
这将按以下顺序排列元素:
-- SQL query
ORDER BY <b>age IS NULL</b>, age DESC
因此,首先按 age IS NULL
排序,这将导致 TRUE
,结果,它被排序在结果的 底部 table.