mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-23 01:57:00 +03:00
Bulk update, allow_add_remove flag
This commit is contained in:
parent
dca24cd914
commit
7eefcf7e53
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user