mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +03:00 
			
		
		
		
	Allow serializers to handle dicts as well as objects. Fixes #447.
This commit is contained in:
		
							parent
							
								
									8113d66126
								
							
						
					
					
						commit
						670ac25b25
					
				| 
						 | 
					@ -29,6 +29,7 @@ You can determine your currently installed version using `pip freeze`:
 | 
				
			||||||
### Master
 | 
					### Master
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Added a `post_save()` hook to the generic views.
 | 
					* Added a `post_save()` hook to the generic views.
 | 
				
			||||||
 | 
					* Allow serializers to handle dicts as well as objects.
 | 
				
			||||||
* Bugfix: Fix styling on browsable API login.
 | 
					* Bugfix: Fix styling on browsable API login.
 | 
				
			||||||
* Bugfix: Fix issue with deserializing empty to-many relations.
 | 
					* Bugfix: Fix issue with deserializing empty to-many relations.
 | 
				
			||||||
* Bugfix: Ensure model field validation is still applied for ModelSerializer subclasses with an custom `.restore_object()` method.
 | 
					* Bugfix: Ensure model field validation is still applied for ModelSerializer subclasses with an custom `.restore_object()` method.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,21 @@ def is_simple_callable(obj):
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_component(obj, attr_name):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Given an object, and an attribute name,
 | 
				
			||||||
 | 
					    return that attribute on the object.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    if isinstance(obj, dict):
 | 
				
			||||||
 | 
					        val = obj[attr_name]
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        val = getattr(obj, attr_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if is_simple_callable(val):
 | 
				
			||||||
 | 
					        return val()
 | 
				
			||||||
 | 
					    return val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Field(object):
 | 
					class Field(object):
 | 
				
			||||||
    read_only = True
 | 
					    read_only = True
 | 
				
			||||||
    creation_counter = 0
 | 
					    creation_counter = 0
 | 
				
			||||||
| 
						 | 
					@ -82,11 +97,9 @@ class Field(object):
 | 
				
			||||||
        if self.source:
 | 
					        if self.source:
 | 
				
			||||||
            value = obj
 | 
					            value = obj
 | 
				
			||||||
            for component in self.source.split('.'):
 | 
					            for component in self.source.split('.'):
 | 
				
			||||||
                value = getattr(value, component)
 | 
					                value = get_component(value, component)
 | 
				
			||||||
                if is_simple_callable(value):
 | 
					 | 
				
			||||||
                    value = value()
 | 
					 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            value = getattr(obj, field_name)
 | 
					            value = get_component(obj, field_name)
 | 
				
			||||||
        return self.to_native(value)
 | 
					        return self.to_native(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def to_native(self, value):
 | 
					    def to_native(self, value):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -325,7 +325,7 @@ class BaseSerializer(Field):
 | 
				
			||||||
        if self.many is not None:
 | 
					        if self.many is not None:
 | 
				
			||||||
            many = self.many
 | 
					            many = self.many
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            many = hasattr(obj, '__iter__') and not isinstance(obj, Page)
 | 
					            many = hasattr(obj, '__iter__') and not isinstance(obj, (Page, dict))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if many:
 | 
					        if many:
 | 
				
			||||||
            return [self.to_native(item) for item in obj]
 | 
					            return [self.to_native(item) for item in obj]
 | 
				
			||||||
| 
						 | 
					@ -343,7 +343,7 @@ class BaseSerializer(Field):
 | 
				
			||||||
            if self.many is not None:
 | 
					            if self.many is not None:
 | 
				
			||||||
                many = self.many
 | 
					                many = self.many
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                many = hasattr(data, '__iter__') and not isinstance(data, dict)
 | 
					                many = hasattr(data, '__iter__') and not isinstance(data, (Page, dict))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # TODO: error data when deserializing lists
 | 
					            # TODO: error data when deserializing lists
 | 
				
			||||||
            if many:
 | 
					            if many:
 | 
				
			||||||
| 
						 | 
					@ -368,7 +368,7 @@ class BaseSerializer(Field):
 | 
				
			||||||
            if self.many is not None:
 | 
					            if self.many is not None:
 | 
				
			||||||
                many = self.many
 | 
					                many = self.many
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                many = hasattr(obj, '__iter__') and not isinstance(obj, Page)
 | 
					                many = hasattr(obj, '__iter__') and not isinstance(obj, (Page, dict))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if many:
 | 
					            if many:
 | 
				
			||||||
                self._data = [self.to_native(item) for item in obj]
 | 
					                self._data = [self.to_native(item) for item in obj]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -185,6 +185,33 @@ class BasicTests(TestCase):
 | 
				
			||||||
        self.assertEquals(instance.age, self.person_data['age'])
 | 
					        self.assertEquals(instance.age, self.person_data['age'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DictStyleSerializer(serializers.Serializer):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Note that we don't have any `restore_object` method, so the default
 | 
				
			||||||
 | 
					    case of simply returning a dict will apply.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    email = serializers.EmailField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DictStyleSerializerTests(TestCase):
 | 
				
			||||||
 | 
					    def test_dict_style_deserialize(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Ensure serializers can deserialize into a dict.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        data = {'email': 'foo@example.com'}
 | 
				
			||||||
 | 
					        serializer = DictStyleSerializer(data=data)
 | 
				
			||||||
 | 
					        self.assertTrue(serializer.is_valid())
 | 
				
			||||||
 | 
					        self.assertEquals(serializer.data, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_dict_style_serialize(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Ensure serializers can serialize dict objects.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        data = {'email': 'foo@example.com'}
 | 
				
			||||||
 | 
					        serializer = DictStyleSerializer(data)
 | 
				
			||||||
 | 
					        self.assertEquals(serializer.data, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ValidationTests(TestCase):
 | 
					class ValidationTests(TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        self.comment = Comment(
 | 
					        self.comment = Comment(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user