From e37619f7410bcfc472db56635aa573b33f83e92a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Aug 2016 13:05:12 +0100 Subject: [PATCH] Serializer defaults should not be included in partial updates. (#4346) Serializer default values should not be included in partial updates --- docs/api-guide/fields.md | 4 +++- rest_framework/fields.py | 3 ++- tests/test_serializer.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index a7ad4f70c..4b566d37e 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -49,7 +49,9 @@ Defaults to `False` ### `default` -If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behavior is to not populate the attribute at all. +If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behaviour is to not populate the attribute at all. + +The `default` is not applied during partial update operations. In the partial update case only fields that are provided in the incoming data will have a validated value returned. May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for [validators](validators.md#using-set_context). diff --git a/rest_framework/fields.py b/rest_framework/fields.py index cbf12010c..704dbaf3f 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -435,7 +435,8 @@ class Field(object): return `empty`, indicating that no value should be set in the validated data for this field. """ - if self.default is empty: + if self.default is empty or getattr(self.root, 'partial', False): + # No default, or this is a partial update. raise SkipField() if callable(self.default): if hasattr(self.default, 'set_context'): diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 741c6ab17..4e9080909 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -309,3 +309,31 @@ class TestCacheSerializerData: pickled = pickle.dumps(serializer.data) data = pickle.loads(pickled) assert data == {'field1': 'a', 'field2': 'b'} + + +class TestDefaultInclusions: + def setup(self): + class ExampleSerializer(serializers.Serializer): + char = serializers.CharField(read_only=True, default='abc') + integer = serializers.IntegerField() + self.Serializer = ExampleSerializer + + def test_default_should_included_on_create(self): + serializer = self.Serializer(data={'integer': 456}) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc', 'integer': 456} + assert serializer.errors == {} + + def test_default_should_be_included_on_update(self): + instance = MockObject(char='def', integer=123) + serializer = self.Serializer(instance, data={'integer': 456}) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc', 'integer': 456} + assert serializer.errors == {} + + def test_default_should_not_be_included_on_partial_update(self): + instance = MockObject(char='def', integer=123) + serializer = self.Serializer(instance, data={'integer': 456}, partial=True) + assert serializer.is_valid() + assert serializer.validated_data == {'integer': 456} + assert serializer.errors == {}