如何用Django ORM的annotate获取JSONField数组长度并支持排序?
解决方案
1. 定义兼容多数据库的JSON数组长度函数
不同数据库对JSON数组长度的原生函数名不同,我们封装一个自定义函数来适配:
from django.db.models import Func, IntegerField class JSONArrayLength(Func): output_field = IntegerField() function = '' def as_postgresql(self, compiler, connection): self.function = 'jsonb_array_length' return super().as_postgresql(compiler, connection) def as_mysql(self, compiler, connection): self.function = 'JSON_LENGTH' return super().as_mysql(compiler, connection) def as_sqlite(self, compiler, connection): self.function = 'json_array_length' return super().as_sqlite(compiler, connection)
2. 配置Admin类实现可排序列
在admin.py中修改MapAdmin,通过数据库注解实现长度计算并支持排序:
from django.contrib import admin from .models import Map @admin.register(Map) class MapAdmin(admin.ModelAdmin): list_display = ['id', 'locations_length'] sortable_by = ['locations_length'] def get_queryset(self, request): qs = super().get_queryset(request) # 在数据库层面注解出locations数组的长度 return qs.annotate( locations_length=JSONArrayLength('locations') ) def locations_length(self, obj): return obj.locations_length # 设置列表表头的显示名称 locations_length.short_description = '位置数量'
关键说明
- 数据库注解
annotate是在SQL层面计算数组长度,天然支持排序,解决了Python访问器无法排序的问题。 - 自定义函数会自动适配PostgreSQL、MySQL、SQLite等主流数据库,无需单独修改代码。
- 直接返回注解值比
len(obj.locations)更高效,尤其在数据量较大时能减少内存消耗。
内容的提问来源于stack exchange,提问作者saschwarz
相关产品推荐
相关产品推荐

