aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2013-03-25 20:26:34 +0000
committerTom Christie2013-03-25 20:26:34 +0000
commit7eefcf7e53f2bc37733a601041f23d354c7729f5 (patch)
treece5c2376f90378f57c64ca49612635b455520508
parentdca24cd91488af62152b7e711cd6869b2d60c0ec (diff)
downloaddjango-rest-framework-7eefcf7e53f2bc37733a601041f23d354c7729f5.tar.bz2
Bulk update, allow_add_remove flag
-rw-r--r--docs/api-guide/serializers.md10
-rw-r--r--rest_framework/serializers.py16
-rw-r--r--rest_framework/tests/serializer_bulk_update.py34
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)