values() 返回 QuerySet,每个元素是 dict,仅含字段值,无模型方法或外键对象;需显式指定外键字段如 'author__name',配合 select_related 有效,prefetch_related 失效;性能优于 all() 因减少字段传输与对象实例化。

values() 返回的是什么类型的数据
values() 不返回模型实例,而是直接返回 QuerySet,其中每个元素是 dict(不是 Model 对象)。这意味着你拿不到方法、外键对象、__str__ 或任何模型层逻辑——只有字段值。如果你后续要调用 get_absolute_url() 或访问 author.name 这类属性,会报 KeyError 或 AttributeError。
- 只传字段名,如
values('id', 'title')→ 每个字典含这两个 key - 不传参数(
values())→ 返回所有字段,但仍是dict,不是对象 - 和
values_list()不同:后者返回tuple或list,可加flat=True
values() 和 select_related()/prefetch_related() 能一起用吗
能,但要注意:外键字段必须显式列出,否则关联字段不会被带出。比如 Post.objects.select_related('author').values('id', 'title', 'author__name') 才有效;如果只写 values('id', 'title'),即使加了 select_related,author__name 也不会出现在结果里。
-
select_related只影响 SQL JOIN,不自动填充values()字段列表 -
prefetch_related在values()下基本失效——因为没模型实例,无法做反向关系缓存 - 想查外键字段,必须用双下划线语法写进
values()参数,如'category__slug'
values() 查询性能比 all() 好在哪
核心是数据库层面减少传输和 ORM 构建开销:只 SELECT 指定字段,不查全文本字段(如 TextField)、不实例化对象、不触发 __init__ 和字段描述符逻辑。尤其在有大字段或大量数据时,效果明显。
- 避免 N+1:配合
select_related+ 显式字段,一条 SQL 解决嵌套字段需求 - 注意陷阱:如果字段上有数据库函数(如
Lower('name')),要用annotate(),不能直接塞进values() - MySQL 8.0+ / PostgreSQL 支持覆盖索引时,
values()字段全在索引中,可能走索引覆盖,不回表
values() 常见报错和绕过方式
最典型的是 FieldError: Cannot resolve keyword 'xxx' into field,通常因字段名拼错、用了未定义的属性、或试图展开多对多中间表字段(values() 不支持 ManyToManyField 的正向展开)。
- 外键字段别名要写全:
'author_id'(字段) vs'author__name'(关联字段) - 聚合字段必须先
annotate(),再出现在values()中,如.annotate(count=Count('comments')).values('title', 'count') - 想按某字段去重?用
values('x').distinct(),但注意 PostgreSQL 要求distinct字段在ORDER BY中出现,否则报错
字段越少、嵌套越浅、关联越明确,values() 越稳。一旦开始混用 annotate、F 表达式、条件聚合,就得盯紧 SQL 输出——str(qs.query) 是唯一靠谱的验证方式。










