代码之家  ›  专栏  ›  技术社区  ›  Stan Reduta

调用嵌套序列化程序的.update()方法

  •  0
  • Stan Reduta  · 技术社区  · 5 年前

    我有一个 JSONField 在我的模型中存储一些配置数据。我想访问这个字段(读和写),能够对内部字段及其值进行部分更新。

    为了示例的目的,让一个模型被调用 MyModel 具有 杰森菲尔德 打电话 config :

    class MyModel(models.Model):
        config = JSONField(default=dict())
        ...
    

    我创建了一个单独的 ViewSet 访问存储在 配置 字段。假定 user 模型具有 ForeignKey 关系 MyMa模型 . 这个的简化版本 视图集 是:

    class ConfigurationFieldViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
    
    serializer_class = MyModelConfigurationSerializer
    
    def get_object(self):
        return self.request.user.my_model
    

    数据存储在 配置 有一个特定的结构和几个可能的内部对象:

    {
        "C1": {"counter": 42, "active": false},
        "C2": {"counter": 13, "active": true}
    }
    

    访问并正确序列化 MyMa模型 实例在所有嵌套级别我都为每个字段级别创建了序列化程序。入盟 配置 在领域 MyMa模型 我正在使用这个序列化程序:

    class MyModelConfigurationSerializer(serializers.ModelSerializer):
        configuration = ConfigurationFieldSerializer(required=True)
    
        class Meta:
            model = MyModel
            fields = ('configuration',)
    

    访问和序列化的第一层 configuration 字段有第二个序列化程序:

    class ConfigurationFieldSerializer(serializers.Serializer):
        C1 = BaseConfigurationSerializer(required=True)
        C2 = BaseConfigurationSerializer(required=True)
    

    最后访问每个 C1 C2 字段有第三个序列化程序:

    class BaseConfigurationSerializer(serializers.Serializer):
    
        counter = serializers.IntegerField(
            required=False,
            help_text=_('Some integer field help text')
        )
        active = serializers.BooleanField(
            required=False,
            help_text=_('Some boolean field description')
        )
    

    上面的代码非常适合读取存储在 配置 字段并正确序列化其内部对象。当我试图执行 PUT 在这个领域。

    如果我超驰 update 方法级别 MyModelConfigurationSerializer ,然后序列化程序验证我提交的数据,但作为一个块,我只能一次性保存所有数据。如果我试图提交一些内部字段,内部序列化程序仍然会正确地接收验证错误。

        def update(self, instance, validated_data):
            instance.configuration = validated_data.get(
                'configuration', instance.configuration
            )
            instance.save()
            return instance
    

    但我不能做的是打电话 更新 内部序列化程序的方法( ConfigurationFieldSerializer BaseConfigurationSerializer 在这种情况下):如果我实现它们 更新 方法,它们根本不会被调用。

    根据 DRF Documentation 可写的嵌套表示是可能的,并且对应 更新 create 方法应该在 更新 在顶级序列化程序上调用。

    0 回复  |  直到 5 年前
        1
  •  0
  •   Geekfish    5 年前

    我最近也遇到过这个问题,而且看起来对于嵌套的可写序列化程序来说,您这样做是“唯一的方法”。

    same DRF docs 你可能已经看到了:

    由于嵌套创建和更新的行为可能不明确,并且可能需要相关模型之间的复杂依赖关系,rest framework 3要求您始终显式地编写这些方法。 默认模型序列化器.CREATE()和.UpDATE()方法不包括对可写嵌套表示的支持。

    但是,还有一些第三方包(如drf writable nested)支持自动可写嵌套表示。

    基本上,这意味着当您进行嵌套时,它甚至不会尝试调用任何嵌套的序列化程序存储方法。

    这看起来有点痛苦,但回想起来,这对应用程序的设计可能更好。您的示例非常简单,但在其他情况下,保存内容的顺序可能很重要。如果 update 每个嵌套的序列化程序都是自动运行的,那么drf必须知道何时保存每一个东西。

    作为一个例子,如果你的例子是关于 create 而不是 更新 ,这意味着您需要首先存储您的模型 MyModel 在上面存储配置之前。但是drf不知道。

    同样也可以很容易地发现,配置实际上是另一个需要保存的相关模型 第一 在你能挽救与它的关系之前 MyMa模型 . 所以drf采取的方法就是告诉你自己做,在根序列化器。

    根据我自己的经验,这也有助于您以后微调性能(例如,在您的情况下,您可以避免保存 MyMa模型 两次。

    最后,如果您想使代码更模块化,您仍然可以这样做(将已验证的数据段发送到不同的处理程序,例如发送到新的 update_configurations() 函数),但不能使用嵌套序列化程序自动完成。