在Django项目开发中,从数据库查询数据后按照指定规则排序是常见需求,不少开发者在使用QuerySet排序时会遇到结果不符合预期的情况,比如排序字段存在空值、多字段排序优先级混乱等问题。下面我们就来详细讲解Django QuerySet的排序相关用法,帮助大家正确实现数据排序。

基础排序方法order_by
Django QuerySet通过order_by方法实现排序,该方法接收排序字段作为参数,默认按照升序排列,如果需要降序则可以在字段名前加-前缀。
首先我们有一个简单的模型定义如下:
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=100)
publish_time = models.DateTimeField(auto_now_add=True)
read_count = models.IntegerField(default=0)
# 允许为空的字段,用于演示空值排序
update_time = models.DateTimeField(null=True, blank=True)查询所有文章按照发布时间升序排列的代码如下:
# 按照publish_time升序排列,旧文章在前
articles = Article.objects.all().order_by('publish_time')
# 按照publish_time降序排列,新文章在前
articles = Article.objects.all().order_by('-publish_time')多字段排序规则
当单个字段无法完全满足排序需求时,可以传入多个字段到order_by方法中,排序优先级按照参数传入的顺序依次降低,即先按第一个字段排序,第一个字段值相同的再按第二个字段排序,以此类推。
比如我们需要先按照阅读量降序排列,阅读量相同的再按照发布时间降序排列,代码实现如下:
# 多字段排序,先按read_count降序,再按publish_time降序
articles = Article.objects.all().order_by('-read_count', '-publish_time')如果要实现部分字段升序、部分字段降序的组合,只需要给对应字段加上或去掉-前缀即可,例如先按阅读量降序,阅读量相同的按发布时间升序:
articles = Article.objects.all().order_by('-read_count', 'publish_time')空值排序处理
当排序字段可能存在空值时,不同数据库对空值的排序规则不同,比如PostgreSQL中默认空值排在最前面,MySQL中默认空值排在最后面。如果我们需要统一空值的排序位置,可以使用NullsFirst和NullsLast表达式。
首先需要从django.db.models导入这两个表达式:
from django.db.models import NullsFirst, NullsLast
让空值排在最前面的示例:
# 按照update_time升序排列,空值排在最前面
articles = Article.objects.all().order_by(NullsFirst('update_time'))让空值排在最后面的示例:
# 按照update_time降序排列,空值排在最后面
articles = Article.objects.all().order_by(NullsLast('-update_time'))排序与其他操作的结合注意事项
在使用QuerySet时,排序和过滤、分页等操作结合时需要注意执行顺序,避免排序失效。
排序与过滤的顺序
QuerySet的filter和order_by的顺序不影响最终结果,因为Django会在查询时合并条件,最终生成正确的SQL语句,不过建议先过滤再排序,这样可以减少排序的数据量,提升查询效率。
# 两种写法结果一致,推荐先过滤再排序
# 写法1:先过滤再排序
articles = Article.objects.filter(read_count__gt=100).order_by('-publish_time')
# 写法2:先排序再过滤
articles = Article.objects.order_by('-publish_time').filter(read_count__gt=100)排序与分页的注意事项
如果需要对排序后的结果进行分页,一定要确保排序规则是稳定的,否则分页时可能会出现数据重复或者遗漏的情况。建议在排序时加上唯一字段(比如主键id)作为最后一个排序字段,保证排序结果的唯一性。
# 加上id作为最后一个排序字段,保证排序稳定
articles = Article.objects.all().order_by('-publish_time', 'id')
# 再对结果进行分页
from django.core.paginator import Paginator
paginator = Paginator(articles, 10)
page = paginator.get_page(1)常见排序误区
很多开发者会误以为在模型定义的Meta类中设置ordering属性后,所有查询都会自动应用排序,实际上如果在查询时显式调用了order_by方法,会覆盖模型层面设置的默认排序规则。
比如模型中的默认排序设置:
class Article(models.Model):
# 字段定义同上
class Meta:
# 模型层面默认按照发布时间降序排列
ordering = ['-publish_time']如果执行如下查询,会使用order_by指定的规则,而不会使用模型的默认排序:
# 不会使用模型的默认排序,而是按照阅读量升序排列
articles = Article.objects.all().order_by('read_count')如果需要清除已经设置的排序规则,可以调用order_by方法不传入任何参数:
# 清除所有排序规则,包括模型默认的ordering articles = Article.objects.all().order_by()