order_by()用字段名正序、加-负序,支持多字段逐级排序和foreignkey双下划线;必须显式指定否则无序;空值默认排前,需注意索引优化与数据库兼容性。

怎么用 order_by() 控制正序和负序
正序直接写字段名,负序在字段名前加 -,比如 order_by('name') 是 A→Z,order_by('-name') 就是 Z→A。这个 - 不是减号,是 Django 的约定符号,不能用 desc 或 ASC 替代。
常见错误是误以为 order_by('name', 'age DESC') 这种 SQL 写法能用 —— 实际会报 FieldError: Cannot resolve keyword 'age DESC' into field。Django 不解析 SQL 关键字,只认 -age。
-
order_by('created_at')→ 按时间从早到晚 -
order_by('-created_at')→ 从晚到早(最新在前) - 对
ForeignKey字段排序:用双下划线,如order_by('author__username'),加-同样生效:order_by('-author__username')
多列排序的写法和执行顺序
传多个参数给 order_by(),Django 按参数顺序逐级排序:先按第一个字段分组,组内再按第二个字段排,依此类推。不是“同时比较所有字段”。
比如 order_by('category', '-pub_date') 的意思是:先按 category 升序排,同一 category 下,再按 pub_date 降序(最新文章在前)。
- 字段顺序很重要:
order_by('status', 'priority')和order_by('priority', 'status')结果通常不同 - 混合方向完全合法:
order_by('user_id', '-score', 'updated_at') - 空值(
NULL)默认排在最前(升序时),如果数据库是 PostgreSQL,可配合NullsLast;但 SQLite 和 MySQL 不支持,得靠Coalesce或额外字段兜底
不写 order_by() 时到底有没有默认顺序
没有。Django QuerySet 默认**不保证任何顺序**,即使数据库表有主键或索引,也不代表查询结果会按 id 排。MySQL 可能碰巧按插入顺序返回,PostgreSQL 更可能随机 —— 这不是 bug,是标准行为。
线上出过太多问题:前端分页看起来正常,换库/升级后列表乱序,或者测试环境和生产环境表现不一致。根本原因就是漏了 order_by()。
- 想按主键升序?必须显式写
order_by('id') - 想取消已有排序(比如从某处继承了 order_by)?用
order_by()空调用清空,但注意:这不会恢复“数据库默认顺序”,而是变成无序 - 聚合或
values()后再order_by(),字段必须出现在values()列表里,否则报FieldError
order_by() 性能和数据库索引的关系
排序本身不慢,慢的是没索引时的文件排序(Using filesort)。Django 不自动生成索引,你得自己在 Meta.indexes 或迁移里加。
例如经常查 order_by('-status', '-updated_at'),就该建联合索引:models.Index(fields=['-status', '-updated_at']) —— 注意,Django 2.2+ 才支持字段前加 - 声明降序索引,旧版本只能建 ['status', 'updated_at'],然后靠数据库优化器决定是否用索引反向扫描。
- 单字段排序,普通索引够用;多字段,优先建联合索引,别堆多个单列索引
-
order_by('?')是真随机,会全表扫描,数据量大时禁止用于列表页 - 使用
distinct()后再order_by(),某些字段可能被数据库拒绝(尤其 MySQL),需确保order_by字段也在distinct的维度内
created_at 都没建索引。










