diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index c2b414d7a..b711596bd 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -286,6 +286,12 @@ class BaseSerializer(WritableField): return reverted_data + def skip_field_validation(self, field, attrs, source): + """ + Checks if a field validation should be skipped. + """ + return source not in attrs and (field.partial or not field.required) + def perform_validation(self, attrs): """ Run `validate_()` and `validate()` methods on the serializer @@ -295,8 +301,9 @@ class BaseSerializer(WritableField): continue source = field.source or field_name - if self.partial and source not in attrs: + if self.skip_field_validation(field_name, field, attrs): continue + try: validate_method = getattr(self, 'validate_%s' % field_name, None) if validate_method: diff --git a/rest_framework/tests/test_serializer.py b/rest_framework/tests/test_serializer.py index 91248ce7c..4621205c5 100644 --- a/rest_framework/tests/test_serializer.py +++ b/rest_framework/tests/test_serializer.py @@ -562,6 +562,7 @@ class ValidationTests(TestCase): class CustomValidationTests(TestCase): class CommentSerializerWithFieldValidator(CommentSerializer): + name = serializers.CharField(max_length=255, required=False) def validate_email(self, attrs, source): attrs[source] @@ -573,6 +574,12 @@ class CustomValidationTests(TestCase): raise serializers.ValidationError("Test not in value") return attrs + def validate_name(self, attrs, source): + value = attrs[source] + if value.isupper(): + raise serializers.ValidationError("Uppercase names not allowed") + return attrs + def test_field_validation(self): data = { 'email': 'tom@example.com', @@ -601,6 +608,35 @@ class CustomValidationTests(TestCase): self.assertFalse(serializer.is_valid()) self.assertEqual(serializer.errors, {'content': ['This field is required.']}) + def test_not_required_missing_data(self): + """ + Make sure that `validate_()` is not called if the field is not required and + the field is missing. + """ + data = { + 'email': 'tom@example.com', + 'content': 'A test comment', + 'created': datetime.datetime(2012, 1, 1) + } + serializer = self.CommentSerializerWithFieldValidator(data=data) + self.assertTrue(serializer.is_valid()) + self.assertEqual(serializer.errors, {'content': ['This field is required.']}) + + def test_not_required_invalid_data(self): + """ + Make sure that `validate_()` is called if the field is present, even if the + field is not required. + """ + data = { + 'name': 'TOM', + 'email': 'tom@example.com', + 'content': 'A test comment', + 'created': datetime.datetime(2012, 1, 1) + } + serializer = self.CommentSerializerWithFieldValidator(data=data) + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors, {'name': ['Uppercase names not allowed']}) + def test_wrong_data(self): """ Make sure that validate_content isn't called if the field input is wrong