Django Rest Framework custom validation errors

2019-04-28 06:22发布

问题:

I'm trying to customize the default validation errors of DRF (3.x) for an account model. My goal is to write a validation function in order to send back a customized error message.

I've tried the following:

class AccountSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = Account
        fields = ('id', 'email', 'password',)

    def validate_password(self, value):
        """
        Validate Password.
        """
        if not value:
            raise serializers.ValidationError("Password cannot be empty!")
        elif len(value) < 5:
            raise serializers.ValidationError("Password to short...")
        return value

The the length validation works fine, but the 'password is empty' validation is never thrown because the default error ('password', [u'This field may not be blank.']) is thrown before.

Is there any option to disable default errors or to force validation by my custom function first?

Thanks for help!

回答1:

You can override the validation errors on a per-field basis by setting the error_messages argument when initializing the field. You need to pass a dictionary with the key being the error message name, and the value being the custom text for the error message.

In your case, you are looking to implement two error messages: required and min_length. You may also need to override blank, which is triggering your current error, unless you set allow_blank=True on the field.

So with those changes, your serializer would become

class AccountSerializer(serializers.ModelSerializer):
    password = serializers.CharField(
        write_only=True,
        required=False,
        min_length=5,
        error_messages={
            "blank": "Password cannot be empty.",
            "min_length": "Password too short.",
        },
    )

    class Meta:
        model = Account
        fields = ('id', 'email', 'password', )

I've replaced your len check with the min_length argument on the password field.

This offloads all of your validation to Django REST framework, which should make upgrades easier in the future. You can still override validate_password if you need additional custom validation, but for now I've removed it since it would be empty.



回答2:

Found a solution:

I had to validate the values with the to_internal_value function because validation is run in a specific order (thanks to Kevin Brown): order of validation

Now my improved code is as follows:

class AccountSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = Account
        fields = ('id', 'email', 'password',)

    def to_internal_value(self, data):
        password = data.get('password')
        """
        Validate Password.
        """
        if not password:
            raise serializers.ValidationError({'password': 'Password cannot be empty!'})
        elif len(password) < 5:
            raise serializers.ValidationError({'password': 'Password to short...'})

        return {
            'password': password
        }

I hope this post is useful for somebody :)