我的模型中有一个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
方法。
我最近也遇到过这个问题,看起来你做这件事的方式是嵌套可写序列化器的“唯一方法”。
从same DRF docs你可能已经看到:
由于嵌套创建和更新的行为可能不明确,并且可能需要相关模型之间的复杂依赖关系,因此REST框架3要求您始终显式编写这些方法。默认的ModelSerializer .create()和.update()方法不包括对可写嵌套表示的支持。
但是,可用的第三方软件包(如DRF Writable Nested)支持自动可写嵌套表示。
基本上它意味着当你有嵌套时,它甚至不会尝试调用任何嵌套的串行器存储方法。
这可能看起来有点痛苦,但回想起来,它可能更适合您的应用程序的设计。您的示例非常简单,但在其他情况下,保存事物的顺序可能很重要。如果每个嵌套序列化程序的update
都是自动运行的,那么DRF必须知道何时保存每个东西。
例如,如果您的示例是关于create
而不是update
,那么这意味着您需要首先存储模型MyModel
,然后再将配置存储在其上。但DRF无法知道这一点。
此外,它很容易就是配置实际上是另一个需要先保存的相关模型,然后才能从MyModel
中保存它的关系。所以DRF在根序列化器上采取了告诉你自己做的路线。
根据我自己的经验,这也有助于您稍后调整性能(例如,在您的情况下,您可以避免两次保存MyModel
)。
最后,如果您想使代码更加模块化,您仍然可以执行此操作(将经过验证的数据段发送给不同的处理程序,例如发送到新的update_configurations()
函数),它不会使用嵌套的序列化程序自动完成。