I have a nested serializer field on a serializer which accepts Multipart/form-data (json + images).
The run_validation
happens to remove the nested serializer field data from the QueryDict.
Here is the code:
class CreateSerializer(ModelSerializer[Dress]):
dress_sizes = SizeSerializer(many=True, required=False)
def run_validation(self, data: Any = ...) -> Any | None:
return super(CreateSerializer, self).run_validation(data)
def validate(self, data):
return super().validate(data)
The view uses a custom parser:
class MultipartJsonParser(MultiPartParser):
def parse(self, stream, media_type=None, parser_context=None):
result = super().parse(
stream, media_type=media_type, parser_context=parser_context
)
data = {}
for key, value in result.data.items():
if type(value) != str:
data[key] = value
continue
if "{" in value or "[" in value:
try:
data[key] = json.loads(value)
except ValueError:
data[key] = value
else:
data[key] = value
qdict = QueryDict('', mutable=True)
qdict.update(data)
return DataAndFiles(qdict, result.files)
Here is the data that I get in the run_validation
method:
< QueryDict: {
'file_name': ['swimsuit.svg'],
'dress_sizes': [
[{
'size': 'xs'
}, {
'size': 's'
}, {
'size': 'm'
}, {
'size': 'l'
}, {
'size': 'xl'
}]
],
'image': [ < TemporaryUploadedFile: swim.svg(image / svg + xml) > ]
} >
After run_validation
the validate
gets following data:
OrderedDict([('file_name', 'swimsuit.svg'), ('image', <
TemporaryUploadedFile: swim.svg(image / svg + xml) > )])
Here the whole dress_sizes
has disappeared. Which is needed to create the list of dress_sizes objects. I have reduced the code to make it readable. Maybe there are some mismatches in the spellings which is not the issue.
Question is how to make the run_validation not remove the list of dress_sizes?
Finally in the create method there is no data for the dress_sizes = []
. Here is create:
def create(self, validated_data: Dict[str, Any]) -> Dress:
dress_sizes = validated_data.pop("dress_sizes", [])
The reason why the run_validation method is removing the nested serializer field data from the QueryDict is that the run_validation method of the ModelSerializer class is designed to validate the fields defined on the serializer class, but not the nested serializers.
One way to solve this issue is to override the to_internal_value method of the serializer to manually instantiate and validate the nested serializer fields. Here is an example:
class CreateSerializer(serializers.ModelSerializer):
dress_sizes = SizeSerializer(many=True, required=False)
def to_internal_value(self, data):
dress_sizes_data = data.pop('dress_sizes', [])
validated_data = super().to_internal_value(data)
# Manually validate and save the nested serializer fields
dress_sizes = []
for size_data in dress_sizes_data:
size_serializer = SizeSerializer(data=size_data)
size_serializer.is_valid(raise_exception=True)
dress_sizes.append(size_serializer.save())
validated_data['dress_sizes'] = dress_sizes
return validated_data
class Meta:
model = Dress
fields = '__all__'
With this implementation, the to_internal_value method is called after the request data is parsed and before validation is performed. It manually extracts the data for the nested serializer fields, validates them, and saves them to the validated_data dictionary. Then it returns the validated data with the nested serializer fields properly validated and saved.
By doing this, the run_validation method of the ModelSerializer class will no longer remove the dress_sizes data from the validated_data dictionary.