JSONField 嵌套表示的序列化程序
Serializer for JSONField nested representation
在我的应用程序中,有一个模型在其字段之一中存储配置。该字段定义为 JSONField
。我有一个严格的结构来定义该字段的内容应该是什么样子,但我正在努力寻找一种方法来序列化它以验证 API 请求中的数据。
目前可行的解决方案,但不验证 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', ...)
虽然我想要实现的是为 config_field
中的嵌套表示 (reference to DRF documentation) 提供一个序列化程序。到目前为止我尝试过的(但不起作用):
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', ...)
这样,POST/PUT 具有配置的对象将是可选的,但如果 config_field
在请求正文中,则应提供整个嵌套对象。
您正在针对 config_field
字段 发送数据 ,因此,您的数据应包含该键。所以有效载荷应该如下
<b>{"config_field": </b>{"some_config_int": 42, "some_config_vars": ["foo", "bar"]}}
更新 1
在序列化程序中使用 DictField()
作为,
<b>VALID_DICT_KEYS = ['foo_1']</b>
class ConfigFieldsSerializer(serializers.Serializer):
some_config_int = serializers.IntegerField(required=True)
some_config_vars = serializers.ListField(<b>child=serializers.DictField()</b>, required=True)
<b>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</b>
class MyModelSerializer(serializers.Serializer):
config_field = ConfigFieldsSerializer(required=False)
class Meta:
fields = ('config_field',)
data = {<b>'config_field'</b>: {"some_config_int": 42, "some_config_vars": [{"foo_1": "bar"}, {"foo_2": "honey"}]}}
serializer = MyModelSerializer(data=data)
serializer.is_valid(True)
print(serializer.data)
在尝试了几种可能的解决方案之后,我想指出 2 个最简单且最重要的解决方案 不需要 覆盖 create
既不用于 MyModelSerializer
也不用于内部序列化程序的方法:
- 覆盖
MyModelSerializer
中 config_field
的字段验证方法
- 重写
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)
Note that some_config_vars
stores list of objects, that's why many=True
.
解决方案 1
覆盖 MyModelSerializer
中 config_field
的字段验证方法。在给定示例的情况下,序列化程序的最终代码将是:
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
此方法首先使用默认 JSONFieldSerializer
验证 config_field
并在内容不是有效的 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
这种方法需要更多的代码,但允许将 config_field
的验证与其他相关字段结合起来。
在我的应用程序中,有一个模型在其字段之一中存储配置。该字段定义为 JSONField
。我有一个严格的结构来定义该字段的内容应该是什么样子,但我正在努力寻找一种方法来序列化它以验证 API 请求中的数据。
目前可行的解决方案,但不验证 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', ...)
虽然我想要实现的是为 config_field
中的嵌套表示 (reference to DRF documentation) 提供一个序列化程序。到目前为止我尝试过的(但不起作用):
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', ...)
这样,POST/PUT 具有配置的对象将是可选的,但如果 config_field
在请求正文中,则应提供整个嵌套对象。
您正在针对 config_field
字段 发送数据 ,因此,您的数据应包含该键。所以有效载荷应该如下
<b>{"config_field": </b>{"some_config_int": 42, "some_config_vars": ["foo", "bar"]}}
更新 1
在序列化程序中使用 DictField()
作为,
<b>VALID_DICT_KEYS = ['foo_1']</b>
class ConfigFieldsSerializer(serializers.Serializer):
some_config_int = serializers.IntegerField(required=True)
some_config_vars = serializers.ListField(<b>child=serializers.DictField()</b>, required=True)
<b>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</b>
class MyModelSerializer(serializers.Serializer):
config_field = ConfigFieldsSerializer(required=False)
class Meta:
fields = ('config_field',)
data = {<b>'config_field'</b>: {"some_config_int": 42, "some_config_vars": [{"foo_1": "bar"}, {"foo_2": "honey"}]}}
serializer = MyModelSerializer(data=data)
serializer.is_valid(True)
print(serializer.data)
在尝试了几种可能的解决方案之后,我想指出 2 个最简单且最重要的解决方案 不需要 覆盖 create
既不用于 MyModelSerializer
也不用于内部序列化程序的方法:
- 覆盖
MyModelSerializer
中 - 重写
validate
方法,用于由MyModelSerializer
序列化的整个对象
config_field
的字段验证方法
表示 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)
Note that
some_config_vars
stores list of objects, that's whymany=True
.
解决方案 1
覆盖 MyModelSerializer
中 config_field
的字段验证方法。在给定示例的情况下,序列化程序的最终代码将是:
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
此方法首先使用默认 JSONFieldSerializer
验证 config_field
并在内容不是有效的 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
这种方法需要更多的代码,但允许将 config_field
的验证与其他相关字段结合起来。