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!
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.
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 :)