diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 95703033a..5aae96a9f 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -570,6 +570,29 @@ This option is a dictionary, mapping field names to a dictionary of keyword argu user.save() return user + +## Return errors on undefined fields + +By default, if the user consuming the your API supplies fields in a payload that you have not defined (eg. fields in a POST payload), Django Rest Framework will ignore them. + +It is possible to have DRF return validation errors for undefined fields supplied by the user. This is done by setting `error_on_undefined=True` in your Meta class. For example: + + class NameSerializer(serializers.Serializer): + first_name = serializers.CharField() + last_name = serializers.Charfield() + + class Meta: + error_on_undefined = True + + serializer = NameSerializer(data={'first_name': 'George' + 'middle_name': 'Walker', + 'last_name': 'Bush'}) + serializer.is_valid() # False + print(serializer.errors) # {'middle_name': 'Not a supported field.'} + +This can be helpful to users of your API as they may not realise that they are supplying an unsupported field. For example they may have a typo in their integration code. + + ## Relational fields When serializing model instances, there are a number of different ways you might choose to represent relationships. The default representation for `ModelSerializer` is to use the primary keys of the related instances. diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 5aef1df69..7f3f94486 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -323,7 +323,8 @@ def get_validation_error_detail(exc): @six.add_metaclass(SerializerMetaclass) class Serializer(BaseSerializer): default_error_messages = { - 'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.') + 'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.'), + 'undefined': _('Not a supported field.') } @property @@ -446,6 +447,13 @@ class Serializer(BaseSerializer): else: set_value(ret, field.source_attrs, validated_value) + if getattr(self, 'Meta', None) and \ + getattr(self.Meta, 'error_on_undefined', False): + undefined_fields = set(data) - set(self.fields) + if undefined_fields: + errors.update({field: self.error_messages['undefined'] + for field in undefined_fields}) + if errors: raise ValidationError(errors) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 741c6ab17..b8aa61f78 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -61,6 +61,18 @@ class TestSerializer: with pytest.raises(AssertionError): serializer.save() + def test_error_on_undefined(self): + class ExampleSerializer(serializers.Serializer): + char = serializers.CharField() + + class Meta(): + error_on_undefined = True + + serializer = ExampleSerializer(data={'char': 'abc', 'foo': 'bar'}) + assert not serializer.is_valid() + assert serializer.validated_data == {} + assert serializer.errors == {'foo': 'Not a supported field.'} + class TestValidateMethod: def test_non_field_error_validate_method(self):