mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 01:47:59 +03:00 
			
		
		
		
	🐛 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.
This commit is contained in:
		
							parent
							
								
									fc70c0862f
								
							
						
					
					
						commit
						8c3f82fb18
					
				| 
						 | 
					@ -338,7 +338,12 @@ class ManyRelatedField(Field):
 | 
				
			||||||
        # We override the default field access in order to support
 | 
					        # We override the default field access in order to support
 | 
				
			||||||
        # lists in HTML forms.
 | 
					        # lists in HTML forms.
 | 
				
			||||||
        if html.is_html_input(dictionary):
 | 
					        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.getlist(self.field_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return dictionary.get(self.field_name, empty)
 | 
					        return dictionary.get(self.field_name, empty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def to_internal_value(self, data):
 | 
					    def to_internal_value(self, data):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,14 @@
 | 
				
			||||||
from .utils import mock_reverse, fail_reverse, BadType, MockObject, MockQueryset
 | 
					 | 
				
			||||||
from django.core.exceptions import ImproperlyConfigured
 | 
					 | 
				
			||||||
from rest_framework import serializers
 | 
					 | 
				
			||||||
from rest_framework.test import APISimpleTestCase
 | 
					 | 
				
			||||||
import pytest
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .utils import mock_reverse, fail_reverse, BadType, MockObject, MockQueryset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestStringRelatedField(APISimpleTestCase):
 | 
					class TestStringRelatedField(APISimpleTestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
| 
						 | 
					@ -134,3 +139,34 @@ class TestSlugRelatedField(APISimpleTestCase):
 | 
				
			||||||
    def test_representation(self):
 | 
					    def test_representation(self):
 | 
				
			||||||
        representation = self.field.to_representation(self.instance)
 | 
					        representation = self.field.to_representation(self.instance)
 | 
				
			||||||
        assert representation == self.instance.name
 | 
					        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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user