diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 43d339da0..fb5cad2c1 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -624,6 +624,7 @@ class ModelSerializerOptions(SerializerOptions): self.model = getattr(meta, 'model', None) self.read_only_fields = getattr(meta, 'read_only_fields', ()) self.write_only_fields = getattr(meta, 'write_only_fields', ()) + self.use_model_error_messages = getattr(meta, 'use_model_error_messages', False) class ModelSerializer(Serializer): @@ -869,6 +870,9 @@ class ModelSerializer(Serializer): if model_field.help_text is not None: kwargs['help_text'] = model_field.help_text + if self.opts.use_model_error_messages and model_field.error_messages is not None: + kwargs['error_messages'] = model_field.error_messages + # TODO: TypedChoiceField? if model_field.flatchoices: # This ModelField contains choices kwargs['choices'] = model_field.flatchoices diff --git a/rest_framework/tests/test_serializer.py b/rest_framework/tests/test_serializer.py index fb2eac0ba..4aaf64365 100644 --- a/rest_framework/tests/test_serializer.py +++ b/rest_framework/tests/test_serializer.py @@ -1974,3 +1974,53 @@ class BoolenFieldTypeTest(TestCase): ''' bfield = self.serializer.get_fields()['started'] self.assertEqual(type(bfield), fields.BooleanField) + + +class ModelErrorMessagesFieldsTests(TestCase): + + def setUp(self): + class ErrorMessageModel(RESTFrameworkModel): + char_field = models.CharField(blank=False, max_length=5, error_messages={ + 'max_length': 'max_length %(limit_value)d', + }) + int_field = models.IntegerField({ + 'required': 'int required model', + 'invalid': 'int invalid', + }) + + class ErrorMessageSerializer(serializers.ModelSerializer): + int_field = serializers.IntegerField(error_messages={ + 'required': 'int required', + }) + + class Meta: + model = ErrorMessageModel + + self.serializer_class = ErrorMessageSerializer + + def error_test(self, data, expected_error_field, expected_message, enabled=False): + self.serializer_class.Meta.use_model_error_messages = enabled + + serializer = self.serializer_class(data=data) + self.assertEqual(serializer.is_valid(), False) + self.assertEqual(len(serializer.errors), 1) + + for (field_name, message) in serializer.errors.items(): + self.assertEqual(expected_error_field, field_name) + self.assertEqual(expected_message, message[0]) + + def test_model_field_required(self): + self.error_test({'int_field': 10, 'char_field': None}, 'char_field', 'This field is required.', enabled=True) + self.error_test({'int_field': 10, 'char_field': None}, 'char_field', 'This field is required.', enabled=False) + + def test_model_field_validation_failure(self): + self.error_test({'int_field': 10, 'char_field': 'abcdef'}, 'char_field', 'max_length 5', enabled=True) + self.error_test({'int_field': 10, 'char_field': 'abcdef'}, 'char_field', 'Ensure this value has at most 5 characters (it has 6).', enabled=False) + + def test_serializer_field_required(self): + self.error_test({'char_field': 'abcde'}, 'int_field', 'int required', enabled=True) + self.error_test({'char_field': 'abcde'}, 'int_field', 'int required', enabled=False) + + def test_serializer_field_validation_failure(self): + self.error_test({'int_field': 'abc', 'char_field': 'abcde'}, 'int_field', 'Enter a whole number.', enabled=True) + self.error_test({'int_field': 'abc', 'char_field': 'abcde'}, 'int_field', 'Enter a whole number.', enabled=False)