应使用 select_related 和 prefetch_related 避免 n+1 查询:前者通过 join 优化外键/一对一关系,后者通过额外查询+python 关联优化多对多/反向一对多关系。

避免 N+1 查询:用 select_related 和 prefetch_related 一次性加载关联数据
典型症状是页面打开慢、数据库查询数暴增,比如循环中反复调用 obj.foreign_key.field。Django ORM 默认不会预取外键或多对多字段,导致每访问一个关联对象就发一次 SQL。
解决思路看关系类型:
-
select_related适用于外键(ForeignKey)和一对一(OneToOneField),底层用JOIN一次查出主表 + 关联表字段;只能向“正向”或“反向一对一/外键”延伸,不支持多对多或反向多对一 -
prefetch_related适用于多对多(ManyToManyField)、反向一对多(ForeignKey的反向关系),底层发额外的SELECT+ Python 级联匹配;支持嵌套(如prefetch_related('author__posts')),但要注意笛卡尔积风险
示例:
Post.objects.select_related('author').all() # 作者信息随 Post 一起查出<br>Author.objects.prefetch_related('post_set').all() # 每个作者附带全部文章列表
只取需要的字段:用 values()、values_list() 或 only()/defer()
全量查询(Model.objects.all())会加载所有字段,包括大文本(TextField)、二进制(BinaryField)甚至 JSON 字段,拖慢网络传输和内存占用。
按场景选方法:
- 只要字典结构数据 → 用
values('id', 'title'),返回QuerySetofdict,不实例化模型,省内存 - 只要元组或单值 → 用
values_list('id', 'title')或values_list('id', flat=True),更轻量 - 仍需模型实例但想跳过某些字段 → 用
only('id', 'title')(显式指定要的)或defer('content', 'raw_data')(显式排除不要的);注意:首次访问被defer的字段会触发额外查询
过滤和排序提前下推:把逻辑写进 filter() 和 order_by(),别在 Python 里 for 循环筛
Django QuerySet 是惰性执行的,filter()、exclude()、order_by() 都会转成 SQL 的 WHERE 和 ORDER BY,由数据库完成计算。一旦你用 list(...) 或 for obj in qs: 把结果取回 Python 再处理,就失去数据库索引和优化能力了。
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
常见错误:
-
[p for p in Post.objects.all() if p.status == 'published']→ 应改写为Post.objects.filter(status='published') -
sorted(posts, key=lambda x: x.created_at)→ 应改写为Post.objects.order_by('created_at')(注意负号表示降序:'-created_at') - 用
len(qs)判数量 → 改用qs.count(),避免把全部数据拉到内存
慎用 count()、exists() 和切片,理解它们的 SQL 行为
count() 和 exists() 虽然都快,但用途不同:前者返回整数,后者返回布尔值且在找到第一条即停止扫描(适合“是否存在”判断)。两者都比 len(qs) 或 bool(qs) 安全得多。
切片(qs[:10])会生成 LIMIT,但注意:qs[5] 这种索引访问会强制执行整个 QuerySet 并取第 6 个元素,等价于 list(qs)[5] —— 千万别这么用。
另一个坑是链式调用顺序:qs.filter(...).order_by(...).select_related(...) 中,select_related 必须在 filter 之后、order_by 之前(如果涉及关联字段排序),否则可能报错或失效。
复杂查询里,explain() 方法(如 Post.objects.filter(...).explain())能直接看到数据库执行计划,比猜靠谱得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










