diff options
| author | Tom Christie | 2013-03-25 20:26:34 +0000 | 
|---|---|---|
| committer | Tom Christie | 2013-03-25 20:26:34 +0000 | 
| commit | 7eefcf7e53f2bc37733a601041f23d354c7729f5 (patch) | |
| tree | ce5c2376f90378f57c64ca49612635b455520508 | |
| parent | dca24cd91488af62152b7e711cd6869b2d60c0ec (diff) | |
| download | django-rest-framework-7eefcf7e53f2bc37733a601041f23d354c7729f5.tar.bz2 | |
Bulk update, allow_add_remove flag
| -rw-r--r-- | docs/api-guide/serializers.md | 10 | ||||
| -rw-r--r-- | rest_framework/serializers.py | 16 | ||||
| -rw-r--r-- | rest_framework/tests/serializer_bulk_update.py | 34 | 
3 files changed, 44 insertions, 16 deletions
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 42e81cad..aaff760e 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -242,17 +242,17 @@ This allows you to write views that update or create multiple items when a `PUT`      # True      serialize.save()  # `.save()` will be called on each updated or newly created instance. -Bulk updates will update any instances that already exist, and create new instances for data items that do not have a corresponding instance. +By default bulk updates will be limited to updating instances that already exist in the provided queryset. -When performing a bulk update you may want any items that are not present in the incoming data to be deleted.  To do so, pass `allow_delete=True` to the serializer. +When performing a bulk update you may want to allow new items to be created, and missing items to be deleted.  To do so, pass `allow_add_remove=True` to the serializer. -    serializer = BookSerializer(queryset, data=data, many=True, allow_delete=True) +    serializer = BookSerializer(queryset, data=data, many=True, allow_add_remove=True)      serializer.is_valid()      # True -    serializer.save()  # `.save()` will be called on each updated or newly created instance. +    serializer.save()  # `.save()` will be called on updated or newly created instances.                         # `.delete()` will be called on any other items in the `queryset`. -Passing `allow_delete=True` ensures that any update operations will completely overwrite the existing queryset, rather than simply updating any objects found in the incoming data.  +Passing `allow_delete=True` ensures that any update operations will completely overwrite the existing queryset, rather than simply updating existing objects.   #### How identity is determined when performing bulk updates diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 6aca2f57..1b2b0821 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -130,14 +130,14 @@ class BaseSerializer(WritableField):      def __init__(self, instance=None, data=None, files=None,                   context=None, partial=False, many=None, -                 allow_delete=False, **kwargs): +                 allow_add_remove=False, **kwargs):          super(BaseSerializer, self).__init__(**kwargs)          self.opts = self._options_class(self.Meta)          self.parent = None          self.root = None          self.partial = partial          self.many = many -        self.allow_delete = allow_delete +        self.allow_add_remove = allow_add_remove          self.context = context or {} @@ -154,8 +154,8 @@ class BaseSerializer(WritableField):          if many and instance is not None and not hasattr(instance, '__iter__'):              raise ValueError('instance should be a queryset or other iterable with many=True') -        if allow_delete and not many: -            raise ValueError('allow_delete should only be used for bulk updates, but you have not set many=True') +        if allow_add_remove and not many: +            raise ValueError('allow_add_remove should only be used for bulk updates, but you have not set many=True')      #####      # Methods to determine which fields to use when (de)serializing objects. @@ -448,6 +448,10 @@ class BaseSerializer(WritableField):                              # Determine which object we're updating                              identity = self.get_identity(item)                              self.object = identity_to_objects.pop(identity, None) +                            if self.object is None and not self.allow_add_remove: +                                ret.append(None) +                                errors.append({'non_field_errors': ['Cannot create a new item, only existing items may be updated.']}) +                                continue                          ret.append(self.from_native(item, None))                          errors.append(self._errors) @@ -457,7 +461,7 @@ class BaseSerializer(WritableField):                      self._errors = any(errors) and errors or []                  else: -                    self._errors = {'non_field_errors': ['Expected a list of items']} +                    self._errors = {'non_field_errors': ['Expected a list of items.']}              else:                  ret = self.from_native(data, files) @@ -508,7 +512,7 @@ class BaseSerializer(WritableField):          else:              self.save_object(self.object, **kwargs) -        if self.allow_delete and self._deleted: +        if self.allow_add_remove and self._deleted:              [self.delete_object(item) for item in self._deleted]          return self.object diff --git a/rest_framework/tests/serializer_bulk_update.py b/rest_framework/tests/serializer_bulk_update.py index afc1a1a9..8b0ded1a 100644 --- a/rest_framework/tests/serializer_bulk_update.py +++ b/rest_framework/tests/serializer_bulk_update.py @@ -98,7 +98,7 @@ class BulkCreateSerializerTests(TestCase):          serializer = self.BookSerializer(data=data, many=True)          self.assertEqual(serializer.is_valid(), False) -        expected_errors = {'non_field_errors': ['Expected a list of items']} +        expected_errors = {'non_field_errors': ['Expected a list of items.']}          self.assertEqual(serializer.errors, expected_errors) @@ -115,7 +115,7 @@ class BulkCreateSerializerTests(TestCase):          serializer = self.BookSerializer(data=data, many=True)          self.assertEqual(serializer.is_valid(), False) -        expected_errors = {'non_field_errors': ['Expected a list of items']} +        expected_errors = {'non_field_errors': ['Expected a list of items.']}          self.assertEqual(serializer.errors, expected_errors) @@ -201,11 +201,12 @@ class BulkUpdateSerializerTests(TestCase):                  'author': 'Haruki Murakami'              }          ] -        serializer = self.BookSerializer(self.books(), data=data, many=True, allow_delete=True) +        serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True)          self.assertEqual(serializer.is_valid(), True)          self.assertEqual(serializer.data, data)          serializer.save()          new_data = self.BookSerializer(self.books(), many=True).data +          self.assertEqual(data, new_data)      def test_bulk_update_and_create(self): @@ -223,13 +224,36 @@ class BulkUpdateSerializerTests(TestCase):                  'author': 'Haruki Murakami'              }          ] -        serializer = self.BookSerializer(self.books(), data=data, many=True, allow_delete=True) +        serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True)          self.assertEqual(serializer.is_valid(), True)          self.assertEqual(serializer.data, data)          serializer.save()          new_data = self.BookSerializer(self.books(), many=True).data          self.assertEqual(data, new_data) +    def test_bulk_update_invalid_create(self): +        """ +        Bulk update serialization without allow_add_remove may not create items. +        """ +        data = [ +            { +                'id': 0, +                'title': 'The electric kool-aid acid test', +                'author': 'Tom Wolfe' +            }, { +                'id': 3, +                'title': 'Kafka on the shore', +                'author': 'Haruki Murakami' +            } +        ] +        expected_errors = [ +            {}, +            {'non_field_errors': ['Cannot create a new item, only existing items may be updated.']} +        ] +        serializer = self.BookSerializer(self.books(), data=data, many=True) +        self.assertEqual(serializer.is_valid(), False) +        self.assertEqual(serializer.errors, expected_errors) +      def test_bulk_update_error(self):          """          Incorrect bulk update serialization should return error data. @@ -249,6 +273,6 @@ class BulkUpdateSerializerTests(TestCase):              {},              {'id': ['Enter a whole number.']}          ] -        serializer = self.BookSerializer(self.books(), data=data, many=True, allow_delete=True) +        serializer = self.BookSerializer(self.books(), data=data, many=True, allow_add_remove=True)          self.assertEqual(serializer.is_valid(), False)          self.assertEqual(serializer.errors, expected_errors)  | 
