我的模型中有一个JSONField
,它存储一些配置数据。我想访问此字段(包括读取和写入),并能够对内部字段及其值进行部分更新。
出于示例目的,让模型称为MyModel
,其中JSONField
称为config
:
class MyModel(models.Model):
config = JSONField(default=dict())
...
我创建了一个单独的ViewSet
来访问存储在config
字段中的信息。假设user
模型与ForeignKey
有MyModel
关系。该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)
最后要访问每个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')
)
上面的代码非常适合读取存储在config
字段中的数据并正确序列化其内部对象。当我尝试在此字段上执行PUT
时,会出现问题。
如果我在update
级别覆盖MyModelConfigurationSerializer
方法,则序列化程序将验证我提交的数据,但将其作为一个块,并且我只能一次保存所有数据。如果我要提交一些内部字段,我仍然会正确地收到内部序列化程序的验证错误。
def update(self, instance, validated_data):
instance.configuration = validated_data.get(
'configuration', instance.configuration
)
instance.save()
return instance
我无法做的是调用内部串行器的update
方法(在这种情况下为ConfigurationFieldSerializer
和BaseConfigurationSerializer
):如果我实现了它们的update
方法,根本就不会打电话。
根据DRF Documentation可能有可写的嵌套表示形式,并且每当在顶级序列化程序上调用update
时,都应调用相应的create
或update
方法。
答案 0 :(得分:0)
我最近也遇到了这个问题,当涉及嵌套可写序列化程序时,看来您的操作方式是“唯一方法”。
在same DRF docs中,您可能已经看到:
由于嵌套创建和更新的行为可能是模棱两可的,并且可能需要相关模型之间的复杂依赖性,因此REST框架3要求您始终明确地编写这些方法。 默认的ModelSerializer .create()和.update()方法不支持对可写嵌套表示形式的支持。
但是,有第三方软件包,例如DRF Writable Nested,它们支持自动可写嵌套表示。
基本上,这意味着当您进行嵌套时,它甚至都不会尝试调用任何嵌套的序列化器存储方法。
这似乎有些痛苦,但是回想起来,它可能对您的应用程序设计更好。您的示例非常简单,但是在其他情况下,保存内容的顺序可能很重要。如果每个嵌套序列化器的update
是自动运行的,则DRF必须以某种方式知道何时保存每件事。
作为示例,如果您的示例是关于create
而不是update
,则意味着您需要先存储模型MyModel
,然后再将配置存储在其上面。但是DRF不知道。
同样容易的是,配置实际上是另一个相关模型,需要先先保存,然后才能从MyModel
保存与其相关的关系。因此,DRF只是告诉您自己在根序列化程序中执行此操作。
根据我自己的经验,这也有助于以后调整性能(例如,您可以避免两次保存MyModel
)。
最后,如果您想使代码更具模块化,您仍然可以做到(将经过验证的数据段发送到不同的处理程序,例如,发送到新的update_configurations()
函数),只是不会做自动使用嵌套的序列化器。