12
class ChildSerializer(serializers.ModelSerializer):


    class Meta:
         model = Child
         fields = '__all__'


class ParentSerializer(serializers.ModelSerializer):
    """
    Serializer for task
    """
    def validate_title(self, data):
        if not data.get('title'):
            raise serializers.ValidationError('Please set title')
        return data

Validate Function is not called when Post ,Also how can i give custom errors to ChildSerializer ,

3
  • validate_title() won't invokes unless you call serializer.is_valid()
    – JPG
    Commented Mar 21, 2018 at 7:31
  • see this DRF Serializer Validation Document
    – JPG
    Commented Mar 21, 2018 at 7:35
  • DRF has a validation level hierarchy, and field validation is at 4 or 5 level based on the source code. Your field validation may not be hit if DRF validations encounters errors first (i.e blank, null field validations...). There is no override!
    – Lemayzeur
    Commented Sep 27, 2021 at 23:20

5 Answers 5

7

I ran into a similar problem where my custom validation field was not being called. I was writing it to bypass an incorrect DRF validation (more details shown below, but not necessary for the answer).

Looking into the DRF source code I found my problem: DRF always validates your field using its code before validating with your custom code.

''' rest-framework/serializers.py '''

for field in fields:
    validate_method = getattr(self, 'validate_' + field.field_name, None)
    primitive_value = field.get_value(data)
    try:
        # DRF validation always runs first!
        # If DRF validator throws, then custom validation is not called
        validated_value = field.run_validation(primitive_value)
        if validate_method is not None:
            # this is your custom validation
            validated_value = validate_method(validated_value)
    except ValidationError as exc:
        errors[field.field_name] = exc.detail
    except DjangoValidationError as exc:
        errors[field.field_name] = get_error_detail(exc)

Answer: Custom validators cannot be used to bypass DRF's validators, as they will always run first and will raise an exception before you can say it is valid.

(for those interested, the validation error I hit was like this: ModelSerializer used for ModelA, which has a OneToOne relation to ModelB. ModelB has a UUID for its pk. DRF throws the error '53abb068-0286-411e-8729-0174635c5d81' is not a valid UUID. when validating, which is incorrect, and really infuriating.)

5

Your ParentSerializer validation method has some issues. Assumes that there is a title field in your ParentSerializer model. For field level validation, you will get the field instead of whole data. That is validate_title function should have title(title field of the data) as parameter not data. So you dont have to check data.get('title') for the existance of title. Reference

class ParentSerializer(serializers.ModelSerializer):
    """
    Serializer for task
    """
    def validate_title(self, title):
        if not title:
            raise serializers.ValidationError('Please set title')
        return title
1

In addition to @sean.hudson's answer I was trying to figure out how to override the child serializer validation.

It might be possible to "skip" or more accurately ignore children serializer validation errors, by overriding to_internal_value of the ParentSerialzer:

class ParentSerializer(serializers.ModelSerializer):
    children = ChildSerializer(many=True)

    def to_internal_value(self, *args, **kwargs):
        try:
            # runs the child serializers
            return super().to_internal_value(*args, **kwargs)
        except ValidationError as e:
            # fails, and then overrides the child errors with the parent error
            return self.validate(self.initial_data)

    def validate(self, attrs):
        errors = {}
        errors['custom_override_error'] = 'this ignores and overrides the children serializer errors'
        if len(errors):
            raise ValidationError(errors)
        return attrs
    class Meta:
        model = Parent
1

My problem was that I had my own custom to_internal_value method. Removing it fixed the issue.

class EventSerializer(serializers.Serializer):
    end_date = serializers.DateTimeField(format=DATE_FORMAT, required=True)
    start_date = serializers.DateTimeField(format=DATE_FORMAT, required=True)
    description = serializers.CharField(required=True)


    def validate_start_date(self, start_date):
        return start_date

    def validate_end_date(self, end_date):
        return end_date

    # def to_internal_value(self, data):
    #     if data.get('start_date', False):
    #         data['start_date'] = datetime.strptime(data['start_date'], DATE_FORMAT)
    #     if data.get('end_date', False):
    #         data['end_date'] = datetime.strptime(data['end_date'], DATE_FORMAT)
    #     return data
1

I would like to add what the official documentation says, I hope it can be of help.

Field-level validation

You can specify custom field-level validation by adding .validate_<field_name> methods to your Serializer subclass. These are similar to the .clean_<field_name> methods on Django forms.

These methods take a single argument, which is the field value that requires validation.

Your validate_<field_name> methods should return the validated value or raise a serializers.ValidationError. For example:

from rest_framework import serializers

class BlogPostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    content = serializers.CharField()

def validate_title(self, value):
    """
    Check that the blog post is about Django.
    """
    if 'django' not in value.lower():
        raise serializers.ValidationError("Blog post is not about Django")
    return value`
1
  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
    – marcel h
    Commented Oct 13, 2021 at 17:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.