Search code examples
djangodjango-rest-frameworkdjango-serializervalidationerror

django rest framweork: combine ValidationError from multiple validation


Let's say I have the following serializers:

class AnswerSerializer(ModelSerializer):
    answer_text=CharField()

    def validate_answer_text(self, value):
        ...
        return value

    def validate(self, value):
        ...
        return value

class QuestionSerializer(ModelSerializer):
    question_text=CharField()
    answer=AnswerSerializer(many=True, read_only=False)

    def validate_question_text(self, value):
        ...
        return value

    def validate(self, value):
        ...
        return value

If validate_answer_text or validate in AnswerSerializer or validate_question_text in QuestionSerializer raise a ValidationError, the validate of QuestionSerializer won't be run. Thus, I cannot explain all the problem of the POST data.

Is there a way to run the validate function of a serializer even if one of it's field validator or children serializer validation failed and then combine all the errors ?

I have tried the following but did not succeed make it work properly. It does run both validate function and other validators but you cannot nest AllErrorSerializer and more importantly, it does not work when you do not have error: you can't save instance because you have inspect serializer.data.

EDIT

Due to Willem Van Onsem answer, I ended up with the following solution:

#serializers.py
class AnswerSerializer(ModelSerializer):
    answer_text=CharField()

    class Meta:
        model=Answer
        ...

    def validate_answer_text(self, value):
        ...
        return value

    def validate(self, value):
        ...
        return value

class QuestionSerializer(ModelSerializer):
    question_text=CharField()
    answer=AnswerSerializer(many=True, read_only=False)
    class Meta:
        model=Question
        ...

    def validate_question_text(self, value):
        ...
        return value

class BasicAnswerSerializer(ModelSerializer):
    answer_text=CharField()

    class Meta:
        model=Answer
        ...

class BusinessRuleValidator(ModelSerializer):
    question_text=CharField()
    answer=BasicAnswerSerializer(many=True, read_only=False)

    class Meta:
        model=Question
        ...

    def validate(self, value):
        ...
        return value

#views.py

class QuestionViewSet(ModelViewSet):
...
    def create(self, request):
        validator = BusinessRuleValidator(data=request.data)
        validator.is_valid()
        serializer = QuestionSerializer(data=request.data)
        serializer.is_valid()
        if (len(validator.errors) or len(serializer.errors)):
            return Response(merge(validators.errors, serializer.errors), status=404)
        serializer.create()
        return Response('created', status=201)
    

Solution

  • It makes no sense to run validate when of of the fields is invalid. Django will first validate the individual fields, and then construct a dictionary that contains the validated data and thus run the .validate(…) method with that validated data.

    But since the data of (at least) one of the fields is invalid, thus thus means that we can not construct such dictionary if valid data, and therefore the precondition of the .validate(…) method no longer holds. In order to fix this, first these fields should be available.

    For example your serializer might have a boolean field. If a value tralse is for example passed to that field, and the field requires to be true or false, then what value should be passed for that field? A random boolean, the string tralse?

    Another field validator can simply require that the field is part of the request. This thus means that if that field validator fails, there is simply no value for that field. So the only sensical thing to do might be to omit it from the validated_data dictionary, but the validate method takes as precondition that all required fields are in the validated_data dictionary. It thus again makes no sense to run validate on that data.