From c3644234cda5c457d72baf1fbf145f12f49a1fa4 Mon Sep 17 00:00:00 2001 From: Mark Aaron Shirley Date: Tue, 20 Nov 2012 11:01:21 -0800 Subject: [PATCH 1/3] Add support for partial serializer updates --- rest_framework/fields.py | 4 ++-- rest_framework/serializers.py | 3 ++- rest_framework/tests/serializer.py | 12 ++++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 01cf5ae3d..d35e918cd 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -148,7 +148,7 @@ class WritableField(Field): self.widget = widget def validate(self, value): - if value in validators.EMPTY_VALUES and self.required: + if value in validators.EMPTY_VALUES and self.required and not self.root.partial: raise ValidationError(self.error_messages['required']) def run_validators(self, value): @@ -186,7 +186,7 @@ class WritableField(Field): if self.default is not None: native = self.default else: - if self.required: + if self.required and not self.root.partial: raise ValidationError(self.error_messages['required']) return diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 2e7e2cf5d..229c1b2cd 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -91,12 +91,13 @@ class BaseSerializer(Field): _options_class = SerializerOptions _dict_class = SortedDictWithMetadata # Set to unsorted dict for backwards compatibility with unsorted implementations. - def __init__(self, instance=None, data=None, files=None, context=None, **kwargs): + def __init__(self, instance=None, data=None, files=None, context=None, partial=False, **kwargs): super(BaseSerializer, self).__init__(**kwargs) self.opts = self._options_class(self.Meta) self.fields = copy.deepcopy(self.base_fields) self.parent = None self.root = None + self.partial = partial self.context = context or {} diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py index d522ef972..882f769c0 100644 --- a/rest_framework/tests/serializer.py +++ b/rest_framework/tests/serializer.py @@ -108,6 +108,18 @@ class BasicTests(TestCase): self.assertTrue(serializer.object is expected) self.assertEquals(serializer.data['sub_comment'], 'And Merry Christmas!') + def test_partial_update(self): + msg = 'Merry New Year!' + partial_data = {'content': msg} + serializer = CommentSerializer(self.comment, data=partial_data) + self.assertEquals(serializer.is_valid(), False) + serializer = CommentSerializer(self.comment, data=partial_data, partial=True) + expected = self.comment + self.assertEqual(serializer.is_valid(), True) + self.assertEquals(serializer.object, expected) + self.assertTrue(serializer.object is expected) + self.assertEquals(serializer.data['content'], msg) + def test_model_fields_as_expected(self): """ Make sure that the fields returned are the same as defined From 3b43d41e918b70e5ce83f7da2caabcae2e1bcd72 Mon Sep 17 00:00:00 2001 From: Mark Aaron Shirley Date: Tue, 20 Nov 2012 15:57:54 -0800 Subject: [PATCH 2/3] Documentation changes for partial serializer updates --- docs/api-guide/serializers.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index a95891449..624c41595 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -77,6 +77,10 @@ When deserializing data, we can either create a new instance, or update an exist serializer = CommentSerializer(data=data) # Create new instance serializer = CommentSerializer(comment, data=data) # Update `instance` +By default, serializers must be passed values for all required fields or they will throw validation errors. You can use the `partial` argument in order to allow partial updates. + + serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True) # Update `instance` with partial data + ## Validation When deserializing data, you always need to call `is_valid()` before attempting to access the deserialized object. If any validation errors occur, the `.errors` and `.non_field_errors` properties will contain the resulting error messages. From 1adfc41dc7c7e50edbf72f87ebf62bae33eb212c Mon Sep 17 00:00:00 2001 From: Mark Aaron Shirley Date: Wed, 21 Nov 2012 09:36:37 -0800 Subject: [PATCH 3/3] partial argument should override required --- rest_framework/fields.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index d35e918cd..b61dcb42a 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -53,6 +53,8 @@ class Field(object): self.parent = parent self.root = parent.root or parent self.context = self.root.context + if self.root.partial: + self.required = False def field_from_native(self, data, files, field_name, into): """ @@ -148,7 +150,7 @@ class WritableField(Field): self.widget = widget def validate(self, value): - if value in validators.EMPTY_VALUES and self.required and not self.root.partial: + if value in validators.EMPTY_VALUES and self.required: raise ValidationError(self.error_messages['required']) def run_validators(self, value): @@ -186,7 +188,7 @@ class WritableField(Field): if self.default is not None: native = self.default else: - if self.required and not self.root.partial: + if self.required: raise ValidationError(self.error_messages['required']) return