JSONField 嵌套表示的序列化器

Sta*_*ute 4 python django django-serializer django-rest-framework

在我的应用程序中,有一个模型将配置存储在其字段之一中。该字段定义为JSONField。我有一个严格的结构来定义该字段的内容应该是什么样子,但我正在努力寻找一种方法来序列化它以验证 API 请求中的数据。

目前有效但不验证内部内容的解决方案config_field是盲目接受任何符合对象要求的内容json

我的模型的简化版本:

class MyModel(models.Model):
    config_field = JSONField(...)
    ...
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,这是存储在中的数据结构的简化版本config_field

{"some_config_int": 42, "some_config_vars": [{"id": 1}, {"id": 2}]}
Run Code Online (Sandbox Code Playgroud)

这是我的序列化器的简化版本:

class MyModelSerializer(serializers.ModelSerializer):
        config_field = serializers.JSONField(required=False)
        class Meta:
            model = MyModel
            fields = ('config_field', ...)
Run Code Online (Sandbox Code Playgroud)

不过,我想要实现的是为内部内容的嵌套表示(参考 DRF 文档config_field)提供一个序列化器。到目前为止我尝试过的(但不起作用):

class ConfigVarsSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=True)

class ConfigFieldsSerializer(serializers.Serializer):
    some_config_int = serializers.IntegerField(required=True)
    some_config_vars = serializers.ListField(child=ConfigVarsSerializer,required=True)

class MyModelSerializer(serializers.ModelSerializer):
            config_field = ConfigFieldsSerializer(required=False)
            class Meta:
                model = MyModel
                fields = ('config_field', ...)
Run Code Online (Sandbox Code Playgroud)

这样,可以选择 POST/PUT 具有配置的对象,但如果config_field位于请求正文中,则应提供整个嵌套对象。

Sta*_*ute 5

在尝试了几种可能的解决方案之后,我想指出 2 个最简单且最重要的解决方案,这些解决方案既不需要内部序列化器也不需要覆盖create方法:MyModelSerializer

  1. config_field覆盖in的字段验证方法MyModelSerializer
  2. 重写validate正在序列化的整个对象的方法MyModelSerializer

对于两种解决方案,表示 的内部内容的序列化器config_field是相同的:

class ConfigVarsSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=True)

class ConfigFieldsSerializer(serializers.Serializer):
    some_config_int = serializers.IntegerField(required=True)
    some_config_vars = serializers.ConfigVarsSerializer(required=True, many=True)
Run Code Online (Sandbox Code Playgroud)

请注意,some_config_vars存储对象列表,这就是many=True.


解决方案1

config_field覆盖in的字段验证方法MyModelSerializer。在给定示例的情况下,序列化器的最终代码将是:

class MyModelSerializer(serializers.ModelSerializer):
        config_field = JSONField(required=False)
        class Meta:
            model = MyModel
            fields = ('config_field', ...)

        def validate_config_field(self, value):
            serializer = ConfigFieldsSerializer(data=value)
            serializer.is_valid(raise_exception=True)
            return value
Run Code Online (Sandbox Code Playgroud)

此方法首先config_field使用默认值进行验证JSONFieldSerializer,如果内容不是有效JSON对象,则引发异常。

如果JSONFieldSerializer没有引发异常,validate_custom_fields则调用它,并将字段的内容传递到ConfigFieldsSerializer并验证其自身和所有嵌套序列化器的所有内容。


解决方案2

重写validate正在序列化的整个对象的方法MyModelSerializer。在给定示例的情况下,序列化器的最终代码将是:

class MyModelSerializer(serializers.ModelSerializer):
        config_field = JSONField(required=False)
        class Meta:
            model = MyModel
            fields = ('config_field', ...)

        def validate(self, attrs):
            config_field = attrs.get('config_field')
            if config_field:
                serializer = ConfigFieldsSerializer(data=config_field)
                serializer.is_valid(raise_exception=True)
            return attrs
Run Code Online (Sandbox Code Playgroud)

这种方法需要更多的代码,但允许将验证与config_field其他相关字段结合起来。