JSONField嵌套表示的序列化器

时间:2019-05-07 15:17:44

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

在我的应用程序中,有一个模型将配置存储在其一个字段中。该字段定义为<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div id="landing-content"> <section class="slider"> <img src="http://i.imgur.com/fVWomWz.png"/> </section> </div>。我有一个严格的结构来定义该字段的内容,但是我正在努力寻找一种方法来对其进行序列化以验证API请求中的数据。

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

我的模型的简化版本:

json

出于这个问题,这是class MyModel(models.Model): config_field = JSONField(...) ... 中存储的数据结构的简化版本:

config_field

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

{"some_config_int": 42, "some_config_vars": [{"id": 1}, {"id": 2}]}

我想实现的是为class MyModelSerializer(serializers.ModelSerializer): config_field = serializers.JSONField(required=False) class Meta: model = MyModel fields = ('config_field', ...) 内部的嵌套表示形式(reference to DRF documentation)提供一个序列化程序。到目前为止,我已经尝试过(但不起作用):

config_field

通过这种方式,POST / PUT具有配置的对象将是可选的,但是如果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', ...) 位于请求的正文中,则应提供整个嵌套对象。

2 个答案:

答案 0 :(得分:1)

您正在针对config_field字段 发送数据 ,因此,您的数据应包含该密钥。因此有效载荷应如下所示

{"config_field": {"some_config_int": 42, "some_config_vars": ["foo", "bar"]}}

Update-1

在序列化程序中将 DictField() 用作

VALID_DICT_KEYS = ['foo_1']


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

    def validate(self, attrs):
        attrs = super().validate(attrs)
        some_config_vars = attrs['some_config_vars']
        keys_list = []
        for item in some_config_vars:
            keys_list.extend(list(item.keys()))
        unwanted_keys = set(keys_list) - set(VALID_DICT_KEYS)
        if unwanted_keys:
            raise serializers.ValidationError("raise error with some msg")
        return attrs


class MyModelSerializer(serializers.Serializer):
    config_field = ConfigFieldsSerializer(required=False)

    class Meta:
        fields = ('config_field',)


data = {'config_field': {"some_config_int": 42, "some_config_vars": [{"foo_1": "bar"}, {"foo_2": "honey"}]}}
serializer = MyModelSerializer(data=data)
serializer.is_valid(True)
print(serializer.data)

答案 1 :(得分:0)

尝试了几种可能的解决方案后,我想指出2个最简单的解决方案,最重要的是 不需要 覆盖create而不是{{1} }或内部序列化器:

  1. 覆盖MyModelSerializerconfig_field的字段验证方法
  2. MyModelSerializer序列化的整个对象覆盖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) 存储对象列表,这就是some_config_vars的原因。


解决方案1 ​​

覆盖many=Trueconfig_field的字段验证方法。在给定示例的情况下,序列化器的最终代码为:

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 验证config_field,如果内容不是有效的JSONFieldSerializer对象,则会引发异常。

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


解决方案2

ConfigFieldsSerializer序列化的整个对象重写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 的验证与其他相关字段结合起来。