drf默认嵌套序列化器只读,因read_only=true跳过验证与保存逻辑;即使移除该参数,drf也不会自动调用子序列化器的create(),仅尝试将字典塞入父字段导致typeerror或静默丢弃。

DRF 默认只支持「只读嵌套」,想让 POST/PUT 请求里带嵌套数据并自动保存,必须手动重写 create() 和 update() 方法,或者引入 drf-writable-nested —— 否则字段会静默丢弃,连报错都没有。
为什么默认的嵌套序列化器不能写入
DRF 的 ModelSerializer 对外键、OneToOneField 或 ForeignKey 关联字段默认只做序列化输出,不处理反向输入。比如你定义了 comments = CommentSerializer(many=True, read_only=True),那前端传来的 {"comments": [...]} 会被直接忽略。
-
read_only=True是关键开关:它让字段跳过验证和保存逻辑 - 即使去掉
read_only=True,DRF 也不会自动调用子序列化器的create()—— 它只会尝试把整个字典塞进父模型字段,结果是TypeError: Object of type dict is not JSON serializable或静默失败 - 多级嵌套(如 Post → Comments → Author)更危险:每层都得自己递归处理,漏一层就断链
手动实现可写嵌套的关键三步
不依赖第三方库时,必须在主序列化器中显式接管关联对象的创建流程。以 Post 带多个 Comment 为例:
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- 在
PostSerializer的Meta.fields中**不要声明comments字段**,否则 DRF 会试图把它当普通字段处理;改用write_only=True字段或直接在create()里从validated_data取原始数据 -
create()方法里先post = Post.objects.create(...),再遍历validated_data.pop('comments', []),逐个调用CommentSerializer(data=...).is_valid(raise_exception=True)并.save(post=post) - 注意外键赋值方式:
Comment.objects.create(..., post=post)而不是post.comments.set([...])—— 后者只适用于已存在的实例,且不触发CommentSerializer的验证逻辑
用 drf-writable-nested 省掉重复劳动
如果你的嵌套层级超过两层,或涉及 OneToOneField 正向/反向、多对多(非 through 模型),drf-writable-nested 是更稳的选择。它本质是替你封装了上面的手动逻辑,但有几个硬约束必须遵守:
- 子序列化器**必须继承
WritableNestedModelSerializer**,不能只是ModelSerializer - 主序列化器的
Meta.model必须与字段名严格对应关系名,比如profile = ProfileSerializer()要求User.profile是有效的OneToOneField反向关系 - POST 数据里嵌套字段名必须和序列化器字段名一致,且结构要扁平——
drf-writable-nested不解析深层嵌套键,比如不支持{"user": {"profile": {...}}}这种三层结构 - 安装后记得在
settings.py的INSTALLED_APPS加上'drf_writable_nested',否则运行时报AttributeError: 'UserSerializer' object has no attribute '_get_serializer_for_field'
嵌套序列化的性能坑:N+1 查询怎么破
嵌套序列化本身不触发数据库查询,但 serializer.data 访问嵌套字段时,如果没预加载关联数据,就会当场触发 N+1。比如 PostSerializer(many=True) 返回 10 篇文章,每篇带 5 条评论,没优化就是 1 + 10×5 = 51 次查询。
- 查列表时用
prefetch_related('comments')(多对一/多对多)或select_related('author')(一对一/外键) -
prefetch_related对many=True字段是必须的;select_related对read_only=True的单对象字段更高效 - 别在序列化器里写
queryset = Comment.objects.select_related('author')—— 这只影响该字段单独序列化,不影响父序列化器的prefetch_related效果 - 用
django-debug-toolbar的 SQL 面板确认是否还有未优化的查询,比猜靠谱
真正麻烦的不是怎么写嵌套,而是搞清「谁负责创建」「谁负责验证」「谁负责关联赋值」——这三个角色在手动实现时全得你指定,在 drf-writable-nested 里则被隐式绑定到字段名和模型关系上。一旦模型关系名改了、字段名拼错了、或忘了加 prefetch_related,问题往往不报错,只表现成数据为空或响应变慢。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










