Repository: encode/django-rest-framework

Issue:
Add `partial=True` argument to not require serializer fields on update
This is a feature request.

Would it be possible to ignore the required flag on fields during deserialization for the purpose of an update?  It seems like marking fields as required makes a lot of sense for creating a new model, but not as much sense when simply updating an existing model.

**Edit**: Tweaked title slightly - @tomchristie



Discussion:
Proper usage of the `PUT` method would require the fields to be included.
What you're suggesting is the expected behavior of the `PATCH` method.

I'm going to close this ticket as a dup of #333.

If you strongly require this behavior for `PUT` I'd still consider this a duplicate - the right thing to do would be to get #333 implemented for `PATCH` support in REST framework and then override your views to instead call `.patch` from your `.put` methods.

Make sense?

Your reasoning and argument makes perfect sense.  But I was actually wanting to suggest a change at the serializer level.  I was thinking it would be nice to be able to do:

``` python
# data is partial data (i.e. missing some required fields)
serializer = MySerializer(obj, data, partial=True) # doesn't cause validation errors
serializer = MySerializer(obj, data) # still causes validation errors
serializer = MySerializer(data=data) # still causes validation errors
```

Probably not a huge win over the following:

``` python
data = MySerializer(obj).data
data.update(partial_data)
serializer = MySerializer(obj, data)
```

@maspwr - Yup, your `partial=True` flag is _exactly_ the serializer API change I was hoping for in order to support PATCH.  Anyway you're right - it makes sense to have that as a separate ticket (and precursor to PATCH implmentation.)

I think it'd be easy enough - the .required flag on serializer fields could simply be ignored if partial is set to `True` on the serializer.  I'd happily accept a pull req for that.

OK, great!  I'll look into it then.

Great! Lemme know if there's anything I can do to help.
Note that you can get to the top level serializer instance from the child field by accessing `self.root`, so you'll probably be checking something like `self.root.partial`.


Resolving patch:
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -54,6 +54,8 @@ def initialize(self, parent, field_name):
         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):
         """
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -91,12 +91,13 @@ class Meta(object):
     _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 {}
 


Test patch:
diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py
--- a/rest_framework/tests/serializer.py
+++ b/rest_framework/tests/serializer.py
@@ -115,6 +115,18 @@ def test_update(self):
         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