V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
fourstring
V2EX  ›  Django

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

  •  
  •   fourstring · 2019-08-15 18:31:26 +08:00 · 4398 次点击
    这是一个创建于 1919 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如果一个 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。

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

    5 条回复    2019-10-01 11:07:22 +08:00
    shyz
        1
    shyz  
       2019-08-15 18:35:13 +08:00
    drf serializer 序列化不是可以关联序列化吗,可以直接关联相关模型类的序列化器
    fourstring
        2
    fourstring  
    OP
       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
        3
    bnm965321  
       2019-08-16 10:20:00 +08:00   ❤️ 2
    没有最佳实践,这也是 RESTful 的不足之处。

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

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


    还有个究极办法,是使用 GraphQL。
    bnm965321
        4
    bnm965321  
       2019-08-16 10:22:07 +08:00   ❤️ 1
    类似的问题知乎也是有的: https://www.zhihu.com/question/337844933
    freakxx
        5
    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。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1160 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 18:57 · PVG 02:57 · LAX 10:57 · JFK 13:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.