请教编写 Django REST Framework 的 ModelSerializer 时处理模型中的关系字段的最佳实践

2019-08-15 18:31:26 +08:00
 fourstring

如果一个 Model 中存在与其他 Model 的关系字段,则 DRF 的 serializer 有两种方式为它们进行序列化:

RelatedField 的共同之处是使用某种标识符来指向所关联的 Model 的实例,而几乎不涉及关联的实例本身的信息( SlugRelatedField 除外,但文档中也指出 Slug Field 最好指定 unique=True,因而某种意义上也是标识符)。而 Nested Representation 则是将所关联的实例的部分甚至全部信息也序列化并输出。

这两种方式各有优缺点。使用 RelatedField 无法得到所关联的对象的信息,如果需要这些信息,前端就需要额外发起请求,带来不必要的连接开销,但优势在于 PrimaryKeyRelatedField/SlugRelatedField/HyperlinkedRelatedField 默认都是 read-write 的。如果创建的 Serializer 需要承担更新、创建新对象等写操作,可以省去大量代码。Nested Representation 能获取到所关联对象的完整信息,便于前端进行数据展示,但 Nested Representation 默认是 ReadOnly 的,如果需要它承担写操作,必须自行编写 create 和 update 方法,这将会带来很多不必要的代码量。

因此我现在的解决方案是对同一个 Model 的读和写操作分别创建不同的 Serializer,读操作使用 Nested Representation,写操作使用 PrimaryKeyRelatedField。

请问我的理解与做法是否有什么问题?是否有其他的最佳实践呢?非常感谢!

4398 次点击
所在节点    Django
5 条回复
shyz
2019-08-15 18:35:13 +08:00
drf serializer 序列化不是可以关联序列化吗,可以直接关联相关模型类的序列化器
fourstring
2019-08-15 18:40:26 +08:00
@shyz #1 是的,Nested Representation 就是这种模式,但是文档指出这种模式必须手动创建 create 和 update 方法,否则不能用于直接的写操作。但是如果在创建和更新模型对象时没有特殊操作的话,这些代码都是高度重复的,会带来大量的冗余代码。相关部分在这里: https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers
bnm965321
2019-08-16 10:20:00 +08:00
没有最佳实践,这也是 RESTful 的不足之处。

按照 RESTful 的定义,应该所有接口都使用 PrimaryKeyRelatedField 或者 HyperlinkRelatedField,但是现实中一些情况下是需要妥协的,一些资源需要改写成 Nested Representation 模式。如果你就单单把数据表映射给前端,就等着被喷吧。

但是什么时候使用 Nested 模式,要根据情况判断。比如一个 article 的 tags,一般不应该让前端去单独请求。


还有个究极办法,是使用 GraphQL。
bnm965321
2019-08-16 10:22:07 +08:00
类似的问题知乎也是有的: https://www.zhihu.com/question/337844933
freakxx
2019-10-01 11:07:22 +08:00
@fourstring
突然翻到这主题

这个是可以读写一致的,并且可以打开字段,

我之前写过几个版本的这玩意。

还写过递归的版本,反 Nested 存储到复层。


---------------

从源代码角度来说,serializer 也是一个 field,

你继承 PrimaryKeyRelatedField ( HyperlinkRelatedField )后改写他的 to_internal_value 和 to_representation,就可以实现了你的需求。

字段调用大概长这样子
items = ObjectRelatedField(
...
serializer_class=ControlItemSerializer
)

实际上就是丢一个渲染的 serializer 进去 to_representation。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/592226

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX