Django 1.9中的JSONField order_by

18
我有以下包含 JSONField 的 Django 模型:
class RatebookDataEntry(models.Model):
    data = JSONField(blank=True, default=[])
    last_update = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name_plural = 'Ratebook data entries'

data 字段包含以下 json 内容:

{
    "annual_mileage": 15000, 
    "description": "LEON DIESEL SPORT COUPE", 
    "body_style": "Coupe", 
    "range_name": "LEON", 
    "co2_gkm_max": 122, 
    "manufacturer_name": "SEAT"
}

我可以按照数据字段之一对查询集进行排序吗?这个查询不起作用。

RatebookDataEntry.objects.all().order_by("data__manufacturer_name")

1
据我所知(在一个queryset上),目前还没有这样的功能,但是这也暗示着你可能需要一个“数据”的对象模型,而不是json。 - Sayse
5
顺便提一下,使用default=list而不是default=[],否则你会得到在不同实例之间共享的相同列表。 - Alasdair
7个回答

27

正如Julien所提到的,Django目前还不支持在JSONField上进行排序。但是可以通过RawSQLPostgreSQL中的jsonb函数来实现。在OP的情况下:

from django.db.models.expressions import RawSQL
RatebookDataEntry.objects.all().order_by(RawSQL("data->>%s", ("manufacturer_name",)))

8
如果您希望进行降序排列,可以引入注释字段并按其排序。像这样:RatebookDataEntry.objects.annotate(manufacturer_name=RawSQL("data->>%s", ("manufacturer_name",)).order_by("-manufacturer_name") - Eugene Prikazchikov
2
以防有人只看已接受的答案,这是 Django 2.1 中的一个功能,请参见我的答案获取链接。 - yekta
1
这种方法不适用于MySQL,有没有针对MySQL的解决方法? - aliasav
2
如果您想对数字值进行排序,请使用 -> 而不是 ->>。(->> 将JSON对象字段作为文本获取) - Burnash

17
自Django 1.11版本以来,可以使用django.contrib.postgres.fields.jsonb.KeyTextTransform代替RawSQL
from django.contrib.postgres.fields.jsonb import KeyTextTransform

qs = RatebookEntry.objects.all()
qs = qs.annotate(manufacturer_name=KeyTextTransform('manufacturer_name', 'data'))
qs = qs.order_by('manufacturer_name')
# or...
qs = qs.order_by('-manufacturer_name')

在 Django 1.10 中,您需要自己子类化 KeyTransform

from django.contrib.postgres.fields.jsonb import KeyTransform

class KeyTextTransform(KeyTransform):
    operator = '->>'
    nested_operator = '#>>'
    _output_field = TextField()
注意:`KeyTransform`和`KeyTextTransform`的区别在于,`KeyTransform`将返回对象的JSON表示形式,而`KeyTextTransform`将返回对象的值。
例如,如果`data`是`{"test": "stuff"}`,则`KeyTextTransform`将返回`'stuff'`,而`KeyTransform`将返回`'"stuff"'`(可以通过`json.loads`进行解析)。

def 我需要的是什么 - n3rd
请直接返回翻译后的文本: - stupidbodo
我的json字段的数据格式如下,我该如何使用它来根据字典中第一个IP地址进行排序?<QuerySet [{'ip_addresses': {'172.19.128.1': 1, '192.168.0.129': 1}}, {'ip_addresses': {'10.248.91.72': 1, '10.248.91.73': 1, '10.248.91.74': 1}}, {'ip_addresses': {'1.1.34.13': 1}}]> - user4981459

14
这个问题(和大部分答案)适用于Django 1.9。但是,Django版本3.1及更高支持在MariaDB、MySQL、Oracle、PostgreSQL和SQLite的最新版本上使用JSONField 有关在JSON字段上创建/维护索引的行为可能因数据库引擎而异,但排序应该使用您在问题中提到的确切语法工作。
RatebookDataEntry.objects.all().order_by("data__manufacturer_name")

请注意,除非进行进一步的过滤,否则这将包括data JSON字段中不存在 manufacturer_name键的数据库行。


10

根据 Daniil Ryzhkov 的回答和 Eugene Prikazchikov 的评论,您可以使用 RawSQLOrderBy,在不注释查询集的情况下按JSON数据字段进行升序和降序排序。此外,您可以通过添加 LOWER 来执行不区分大小写的排序:

from django.db.models.expressions import RawSQL, OrderBy

RatebookDataEntry.objects.all().order_by(OrderBy(RawSQL("LOWER(data->>%s)", ("manufacturer_name",)), descending=True))

要比较整数字段,您可以将其强制转换为整数:

RatebookDataEntry.objects.all().order_by(OrderBy(RawSQL("cast(data->>%s as integer)", ("annual_mileage",)), descending=True))

6

请参考以下链接:https://docs.djangoproject.com/en/3.2/ref/models/querysets/#order-by - Tim Richardson

3

文档中没有提到这种可能性。目前似乎不能使用基于JSON字段的order_by。


0
我必须按日期排序(使用to_date)执行以下操作。假设data中还有另一个值叫做created_date(例如03.06.2019)。
RatebookDataEntry.objects.all().order_by(
        OrderBy(
            RawSQL("to_date(values->>%s, 'DD.MM.YYYY')", ("created_date",)),
            descending=True,
        )
    )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接