diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index bd54db4ca..cc645df5e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -118,6 +118,7 @@ class BaseSerializer(Field): self._data = None self._files = None self._errors = None + self._instance = instance ##### # Methods to determine which fields to use when (de)serializing objects. @@ -257,7 +258,7 @@ class BaseSerializer(Field): You should override this method to control how deserialized objects are instantiated. """ - if instance is not None: + if instance is not None and not hasattr(instance, '__iter__'): instance.update(attrs) return instance return attrs @@ -276,7 +277,12 @@ class BaseSerializer(Field): """ if hasattr(data, '__iter__') and not isinstance(data, dict): # TODO: error data when deserializing lists - return (self.from_native(item) for item in data) + objects = [] + for i, item in enumerate(data): + if hasattr(self.object, '__iter__'): + self._instance = self.object[i] + objects.append(self.from_native(item, files)) + return objects self._errors = {} if data is not None or files is not None: @@ -286,7 +292,7 @@ class BaseSerializer(Field): self._errors['non_field_errors'] = ['No input provided'] if not self._errors: - return self.restore_object(attrs, instance=getattr(self, 'object', None)) + return self.restore_object(attrs, instance=getattr(self, '_instance', None)) def field_to_native(self, obj, field_name): """ @@ -337,7 +343,11 @@ class BaseSerializer(Field): """ Save the deserialized object and return it. """ - self.object.save() + if hasattr(self.object, '__iter__'): + for obj in self.object: + obj.save() + else: + self.object.save() return self.object diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py index 8767385eb..e0856bff2 100644 --- a/rest_framework/tests/serializer.py +++ b/rest_framework/tests/serializer.py @@ -34,7 +34,7 @@ class CommentSerializer(serializers.Serializer): sub_comment = serializers.Field(source='get_sub_comment.sub_comment') def restore_object(self, data, instance=None): - if instance is None: + if instance is None or hasattr(instance, '__iter__'): return Comment(**data) for key, val in data.items(): setattr(instance, key, val) @@ -881,3 +881,58 @@ class NestedSerializerContextTests(TestCase): # This will raise RuntimeError if context doesn't get passed correctly to the nested Serializers AlbumCollectionSerializer(album_collection, context={'context_item': 'album context'}).data + + +class MultipleObjectsTests(TestCase): + def setUp(self): + self.comments = [Comment( + 'tom@example.com', + 'Happy new year!', + datetime.datetime(2012, 1, 1) + ), + Comment( + 'seb@example.com', + 'Thank you!', + datetime.datetime(2012, 1, 2) + )] + self.data = [{ + 'email': 'tom@example.com', + 'content': 'Happy new year!', + 'created': datetime.datetime(2012, 1, 1), + 'sub_comment': 'This wont change' + }, + { + 'email': 'seb@example.com', + 'content': 'Thank you!', + 'created': datetime.datetime(2012, 1, 2), + 'sub_comment': 'This wont change' + }] + self.expected = [{ + 'email': 'tom@example.com', + 'content': 'Happy new year!', + 'created': datetime.datetime(2012, 1, 1), + 'sub_comment': 'And Merry Christmas!' + }, + { + 'email': 'seb@example.com', + 'content': 'Thank you!', + 'created': datetime.datetime(2012, 1, 2), + 'sub_comment': 'And Merry Christmas!' + }] + + def test_create(self): + serializer = CommentSerializer(data=self.data) + expected = self.comments + self.assertEquals(serializer.is_valid(), True) + self.assertEquals(serializer.object, expected) + self.assertFalse(serializer.object is expected) + self.assertEquals(serializer.data, self.expected) + + def test_update(self): + serializer = CommentSerializer(instance=self.comments, data=self.data) + expected = self.comments + self.assertEquals(serializer.is_valid(), True) + self.assertEquals(serializer.object, expected) + for obj, exp in zip(serializer.object, expected): + self.assertTrue(obj is exp) + self.assertEquals(serializer.data, self.expected)