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

时间:2019-04-16 12:25:39

标签: python django django-rest-framework django-serializer

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

出于示例目的,让模型称为MyModel,其中JSONField称为config

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

我创建了一个单独的ViewSet来访问存储在config字段中的信息。假设user模型与ForeignKeyMyModel关系。该ViewSet的简化版本是:

class ConfigurationFieldViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):

serializer_class = MyModelConfigurationSerializer

def get_object(self):
    return self.request.user.my_model

存储在config中的数据具有某些内部对象的特定结构:

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

要在所有嵌套级别访问MyModel实例并正确序列化,我为每个字段级别创建了序列化器。要访问config本身的MyModel字段,我使用了以下序列化器:

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)

最后要访问每个C1C2字段的内部结构,还有第三个序列化器:

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')
    )

上面的代码非常适合读取存储在config字段中的数据并正确序列化其内部对象。当我尝试在此字段上执行PUT时,会出现问题。

如果我在update级别覆盖MyModelConfigurationSerializer方法,则序列化程序将验证我提交的数据,但将其作为一个块,并且我只能一次保存所有数据。如果我要提交一些内部字段,我仍然会正确地收到内部序列化程序的验证错误。

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

我无法做的是调用内部串行器的update方法(在这种情况下为ConfigurationFieldSerializerBaseConfigurationSerializer):如果我实现了它们的update方法,根本就不会打电话。

根据DRF Documentation可能有可写的嵌套表示形式,并且每当在顶级序列化程序上调用update时,都应调用相应的createupdate方法。

1 个答案:

答案 0 :(得分:0)

我最近也遇到了这个问题,当涉及嵌套可写序列化程序时,看来您的操作方式是“唯一方法”。

same DRF docs中,您可能已经看到:

  

由于嵌套创建和更新的行为可能是模棱两可的,并且可能需要相关模型之间的复杂依赖性,因此REST框架3要求您始终明确地编写这些方法。 默认的ModelSerializer .create()和.update()方法不支持对可写嵌套表示形式的支持。

     

但是,有第三方软件包,例如DRF Writable Nested,它们支持自动可写嵌套表示。

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

这似乎有些痛苦,但是回想起来,它可能对您的应用程序设计更好。您的示例非常简单,但是在其他情况下,保存内容的顺序可能很重要。如果每个嵌套序列化器的update是自动运行的,则DRF必须以某种方式知道何时保存每件事。

作为示例,如果您的示例是关于create而不是update,则意味着您需要先存储模型MyModel,然后再将配置存储在其上面。但是DRF不知道。

同样容易的是,配置实际上是另一个相关模型,需要先先保存,然后才能从MyModel保存与其相关的关系。因此,DRF只是告诉您自己在根序列化程序中执行此操作。

根据我自己的经验,这也有助于以后调整性能(例如,您可以避免两次保存MyModel)。

最后,如果您想使代码更具模块化,您仍然可以做到(将经过验证的数据段发送到不同的处理程序,例如,发送到新的update_configurations()函数),只是不会做自动使用嵌套的序列化器。