values() 返回字典而非模型实例,跳过ORM层开销,适合导出、统计等轻量查询;only() 仍返回完整模型实例,仅延迟加载字段,误用会引发N+1查询。

values() 返回字典而非模型实例,能跳过ORM层开销
当你只需要字段值而不需要模型方法、信号或关系对象时,values() 会直接从数据库取原始数据并构造成 dict,绕过 Django 的模型实例化流程。这省掉了字段类型转换、属性代理、_state 初始化等内存和 CPU 开销。
常见错误是误以为 values('id', 'name') 和 only('id', 'name') 效果一样——其实后者仍返回完整模型实例,只是延迟加载其他字段;前者连 __init__ 都不调用。
- 适合场景:后台导出、统计聚合、API 序列化前的轻量查询
- 注意:
values()结果不能调用模型方法(如get_absolute_url()),也不能访问反向关系(如user.profile) - 若需保持字段类型(如
DateTimeField仍是datetime对象),它仍会做基础转换;但不会触发自定义to_python或from_db
only() 只控制字段加载时机,不减少实例数量
only() 的作用是告诉 Django:“只从数据库查这几个字段,其余字段等真正访问时再懒加载”。但它依然为每行结果创建一个完整的 Model 实例,所有 ORM 内部状态(如 _deferred 标记、字段缓存字典)都存在。
容易踩的坑是:在循环中访问未 only() 的字段(比如 obj.created_at),会触发 N+1 查询——尤其当 created_at 是 DateTimeField 且数据库里为空时,Django 还要额外处理 NULL 转换逻辑。
立即学习“Python免费学习笔记(深入)”;
- 性能影响取决于字段数和实例数:10 万条记录用
only('id'),内存占用仍可能是values('id')的 2–3 倍 -
only()和defer()互斥;多次调用only()会覆盖前一次(不是叠加) - 对
ForeignKey字段,only('author_id')不会加载author对象,但only('author')会——这是常见误用点
values() 与 only() 混用无意义,且可能引发意外行为
在同一个 QuerySet 上链式调用 .values().only(...) 或 .only().values(...),Django 会忽略 only() ——因为 values() 已切换结果形态为字典,only() 的字段延迟逻辑失去作用对象。
实操建议:明确目的再选方法。如果目标是“最小化单次查询内存”,优先用 values();如果后续需要复用模型方法或准备做 save(),才考虑 only(),并确保不触碰未加载字段。
- 错误示例:
User.objects.only('username').values('id')→only()被静默丢弃 - 正确替代:
User.objects.values('id', 'username')(更清晰,更省内存) - 若需保留主键用于后续 update,
values()里必须显式包含'id',否则无法调用save()
大数据量下 values_list() 往往比 values() 更省内存
当只需要一两个字段且不关心字段名时,values_list('id', flat=True) 返回的是 tuple 或 list,比 values() 的 dict 少一层哈希表结构和字符串 key 开销。尤其在 10 万+ 行时,内存差异可达 MB 级别。
注意 flat=True 仅适用于单字段;多字段时返回 tuple,虽不如 dict 直观,但序列化和遍历更快。
- 典型场景:生成 ID 列表用于批量删除:
ids = list(User.objects.filter(active=False).values_list('id', flat=True)) - 避免写成:
[u['id'] for u in User.objects.filter(...).values('id')]—— 多一次字典索引和列表推导开销 - 如果字段含
NULL,values_list()返回 PythonNone,无需额外处理
values() 在数据库结果解析阶段就切断了模型层,而 only() 是在模型实例已存在后做字段访问拦截——这个根本差异,决定了它们适用的边界。


















