Python 调用嵌套序列化程序';s.update()方法
我的模型中有一个Python 调用嵌套序列化程序';s.update()方法,python,django,django-rest-framework,django-serializer,Python,Django,Django Rest Framework,Django Serializer,我的模型中有一个JSONField,用于存储一些配置数据。我想访问这个字段(读和写),能够对内部字段及其值进行部分更新 例如,让一个模型被称为MyModel,而JSONField被称为config: class MyModel(models.Model): config = JSONField(default=dict()) ... 我创建了一个单独的ViewSet来访问config字段中存储的信息。假设user模型与MyModel有ForeignKey关系。此视图集的简化版本
JSONField
,用于存储一些配置数据。我想访问这个字段(读和写),能够对内部字段及其值进行部分更新
例如,让一个模型被称为MyModel
,而JSONField
被称为config
:
class MyModel(models.Model):
config = JSONField(default=dict())
...
我创建了一个单独的ViewSet
来访问config
字段中存储的信息。假设user
模型与MyModel
有ForeignKey
关系。此视图集的简化版本为:
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
实例,我为每个字段级别创建了序列化程序。要访问MyModel
本身中的config
字段,我使用此序列化程序:
class MyModelConfigurationSerializer(serializers.ModelSerializer):
configuration = ConfigurationFieldSerializer(required=True)
class Meta:
model = MyModel
fields = ('configuration',)
class ConfigurationFieldSerializer(serializers.Serializer):
C1 = BaseConfigurationSerializer(required=True)
C2 = BaseConfigurationSerializer(required=True)
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')
)
要访问和序列化配置的第一层
字段,有第二个序列化程序:
class MyModelConfigurationSerializer(serializers.ModelSerializer):
configuration = ConfigurationFieldSerializer(required=True)
class Meta:
model = MyModel
fields = ('configuration',)
class ConfigurationFieldSerializer(serializers.Serializer):
C1 = BaseConfigurationSerializer(required=True)
C2 = BaseConfigurationSerializer(required=True)
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')
)
最后要访问每个C1
和C2
字段的内部结构,有第三个序列化程序:
class MyModelConfigurationSerializer(serializers.ModelSerializer):
configuration = ConfigurationFieldSerializer(required=True)
class Meta:
model = MyModel
fields = ('configuration',)
class ConfigurationFieldSerializer(serializers.Serializer):
C1 = BaseConfigurationSerializer(required=True)
C2 = BaseConfigurationSerializer(required=True)
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
时,会出现此问题
如果我在MyModelConfigurationSerializer
级别重写update
方法,那么序列化程序将验证我提交的数据,但将其作为块,并且我只能一次保存所有数据。如果我试图提交一些内部字段,我仍然正确地收到内部序列化程序的验证错误
def update(self, instance, validated_data):
instance.configuration = validated_data.get(
'configuration', instance.configuration
)
instance.save()
return instance
但是我不能做的是调用内部序列化程序的update
方法(ConfigurationFieldSerializer
和BaseConfigurationSerializer
),如果我实现了它们的update
方法,它们就不会被调用
根据可写性,嵌套表示是可能的,当在顶级序列化程序上调用update
时,应该调用相应的update
或create
方法。我最近也遇到了这个问题,看起来您这样做是“唯一的方法”当涉及到嵌套的可写序列化程序时
def update(self, instance, validated_data):
instance.configuration = validated_data.get(
'configuration', instance.configuration
)
instance.save()
return instance
从中您可能已经看到:
由于嵌套创建和更新的行为可能不明确,并且可能需要相关模型之间的复杂依赖关系,REST framework 3要求您始终显式编写这些方法默认的ModelSerializer.create()和.update()方法不支持可写嵌套表示。
但是,也有第三方软件包,例如支持自动可写嵌套表示的DRF Writable Nested
基本上,这意味着当您进行嵌套时,它甚至不会尝试调用任何嵌套的序列化程序存储方法
这似乎有点痛苦,但回想起来,这对应用程序的设计可能更好。您的示例非常简单,但在其他情况下,保存内容的顺序可能很重要。如果自动运行每个嵌套序列化程序的update
,则DRF必须知道何时保存每个内容
例如,如果您的示例是关于create
而不是update
,则意味着您需要先存储模型MyModel
,然后再将配置存储在其上。然而,DRF不知道这一点
同样很容易的是,配置实际上是另一个相关模型,需要先保存它,然后才能从MyModel
保存与它的关系。因此,DRF采取的路线只是告诉您自己在根序列化程序中完成它
根据我自己的经验,这也有助于您以后对性能进行微调(例如,在您的情况下,您可以避免保存MyModel
两次)
最后,如果您想使代码更加模块化,您仍然可以这样做(将已验证数据的段发送到不同的处理程序,例如发送到新的update\u configurations()
函数),它只是不会使用嵌套序列化器自动完成。我最近也遇到了这个问题,对于嵌套的可写序列化程序,您这样做似乎是“唯一的方法”
def update(self, instance, validated_data):
instance.configuration = validated_data.get(
'configuration', instance.configuration
)
instance.save()
return instance
从中您可能已经看到:
由于嵌套创建和更新的行为可能不明确,并且可能需要相关模型之间的复杂依赖关系,REST framework 3要求您始终显式编写这些方法默认的ModelSerializer.create()和.update()方法不支持可写嵌套表示。
但是,也有第三方软件包,例如支持自动可写嵌套表示的DRF Writable Nested
基本上,这意味着当您进行嵌套时,它甚至不会尝试调用任何嵌套的序列化程序存储方法
这似乎有点痛苦,但回想起来,这对应用程序的设计可能更好。您的示例非常简单,但在其他情况下,保存内容的顺序可能很重要。如果自动运行每个嵌套序列化程序的update
,则DRF必须知道何时保存每个内容
例如,如果您的示例是关于create
而不是update
,则意味着您需要先存储模型MyModel
,然后再将配置存储在其上。然而,DRF不知道这一点
同样很容易的是,配置实际上是另一个相关模型,需要先保存,然后才能从保存与它的关系