aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSusan Dreher2015-01-27 16:18:51 -0500
committerSusan Dreher2015-01-27 16:18:51 -0500
commit8c3f82fb18a58b8e0983612ef3cc35b3c3950b66 (patch)
treef20362c16d420200a983cadf88e99a24de1b775c
parentfc70c0862ff3e6183b79adc4675a63874261ddf0 (diff)
downloaddjango-rest-framework-8c3f82fb18a58b8e0983612ef3cc35b3c3950b66.tar.bz2
:bug: ManyRelatedField get_value clearing field on partial update
A PATCH to a serializer's non-related CharField was clearing an ancillary StringRelatedField(many=True) field. The issue appears to be in the ManyRelatedField's get_value method, which was returning a [] instead of empty when the request data was a MultiDict. This fix mirrors code in fields.py, class Field, get_value, Ln. 272, which explicitly returns empty on a partial update. Tests added to demonstrate the issue.
-rw-r--r--rest_framework/relations.py5
-rw-r--r--tests/test_relations.py40
2 files changed, 43 insertions, 2 deletions
diff --git a/rest_framework/relations.py b/rest_framework/relations.py
index aa0c2def..13793f37 100644
--- a/rest_framework/relations.py
+++ b/rest_framework/relations.py
@@ -338,7 +338,12 @@ class ManyRelatedField(Field):
# We override the default field access in order to support
# lists in HTML forms.
if html.is_html_input(dictionary):
+ # Don't return [] if the update is partial
+ if self.field_name not in dictionary:
+ if getattr(self.root, 'partial', False):
+ return empty
return dictionary.getlist(self.field_name)
+
return dictionary.get(self.field_name, empty)
def to_internal_value(self, data):
diff --git a/tests/test_relations.py b/tests/test_relations.py
index 62353dc2..143e835c 100644
--- a/tests/test_relations.py
+++ b/tests/test_relations.py
@@ -1,8 +1,13 @@
-from .utils import mock_reverse, fail_reverse, BadType, MockObject, MockQueryset
+import pytest
+
from django.core.exceptions import ImproperlyConfigured
+from django.utils.datastructures import MultiValueDict
+
from rest_framework import serializers
+from rest_framework.fields import empty
from rest_framework.test import APISimpleTestCase
-import pytest
+
+from .utils import mock_reverse, fail_reverse, BadType, MockObject, MockQueryset
class TestStringRelatedField(APISimpleTestCase):
@@ -134,3 +139,34 @@ class TestSlugRelatedField(APISimpleTestCase):
def test_representation(self):
representation = self.field.to_representation(self.instance)
assert representation == self.instance.name
+
+
+class TestManyRelatedField(APISimpleTestCase):
+ def setUp(self):
+ self.instance = MockObject(pk=1, name='foo')
+ self.field = serializers.StringRelatedField(many=True)
+ self.field.field_name = 'foo'
+
+ def test_get_value_regular_dictionary_full(self):
+ assert 'bar' == self.field.get_value({'foo': 'bar'})
+ assert empty == self.field.get_value({'baz': 'bar'})
+
+ def test_get_value_regular_dictionary_partial(self):
+ setattr(self.field.root, 'partial', True)
+ assert 'bar' == self.field.get_value({'foo': 'bar'})
+ assert empty == self.field.get_value({'baz': 'bar'})
+
+ def test_get_value_multi_dictionary_full(self):
+ mvd = MultiValueDict({'foo': ['bar1', 'bar2']})
+ assert ['bar1', 'bar2'] == self.field.get_value(mvd)
+
+ mvd = MultiValueDict({'baz': ['bar1', 'bar2']})
+ assert [] == self.field.get_value(mvd)
+
+ def test_get_value_multi_dictionary_partial(self):
+ setattr(self.field.root, 'partial', True)
+ mvd = MultiValueDict({'foo': ['bar1', 'bar2']})
+ assert ['bar1', 'bar2'] == self.field.get_value(mvd)
+
+ mvd = MultiValueDict({'baz': ['bar1', 'bar2']})
+ assert empty == self.field.get_value(mvd)