aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Aaron Shirley2013-04-18 10:28:20 -0700
committerMark Aaron Shirley2013-04-18 10:28:20 -0700
commitfdc5cc3d81679d30cd20acf063dc7dc74ad17d7a (patch)
treefcc112e4a6c25495989cee5de84cb9658f604509
parentc7e000e46e831a254689faac44ea44ebafe3cd61 (diff)
downloaddjango-rest-framework-fdc5cc3d81679d30cd20acf063dc7dc74ad17d7a.tar.bz2
Fix model serializer nestesd delete behavior
-rw-r--r--rest_framework/serializers.py40
-rw-r--r--rest_framework/tests/relations_nested.py19
2 files changed, 33 insertions, 26 deletions
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 0f0f11a4..78c45548 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -20,6 +20,9 @@ from rest_framework.relations import *
from rest_framework.fields import *
+class RelationsList(list):
+ _deleted = []
+
class NestedValidationError(ValidationError):
"""
The default ValidationError behavior is to stringify each item in the list
@@ -149,7 +152,6 @@ class BaseSerializer(WritableField):
self._data = None
self._files = None
self._errors = None
- self._deleted = None
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')
@@ -288,15 +290,8 @@ class BaseSerializer(WritableField):
You should override this method to control how deserialized objects
are instantiated.
"""
- removed_relations = []
-
- # Deleted related objects
- if self._deleted:
- removed_relations = list(self._deleted)
-
if instance is not None:
instance.update(attrs)
- instance._removed_relations = removed_relations
return instance
return attrs
@@ -438,7 +433,7 @@ class BaseSerializer(WritableField):
PendingDeprecationWarning, stacklevel=3)
if many:
- ret = []
+ ret = RelationsList()
errors = []
update = self.object is not None
@@ -466,7 +461,7 @@ class BaseSerializer(WritableField):
errors.append(self._errors)
if update:
- self._deleted = identity_to_objects.values()
+ ret._deleted = identity_to_objects.values()
self._errors = any(errors) and errors or []
else:
@@ -509,9 +504,6 @@ class BaseSerializer(WritableField):
def save_object(self, obj, **kwargs):
obj.save(**kwargs)
- if self.allow_add_remove and hasattr(obj, '_removed_relations'):
- [self.delete_object(item) for item in obj._removed_relations]
-
def delete_object(self, obj):
obj.delete()
@@ -521,11 +513,11 @@ class BaseSerializer(WritableField):
"""
if isinstance(self.object, list):
[self.save_object(item, **kwargs) for item in self.object]
- else:
- self.save_object(self.object, **kwargs)
- if self.allow_add_remove and self._deleted:
- [self.delete_object(item) for item in self._deleted]
+ if self.allow_add_remove and self.object._deleted:
+ [self.delete_object(item) for item in self.object._deleted]
+ else:
+ self.save_object(self.object, **kwargs)
return self.object
@@ -715,7 +707,6 @@ class ModelSerializer(Serializer):
m2m_data = {}
related_data = {}
nested_forward_relations = {}
- removed_relations = []
meta = self.opts.model._meta
# Reverse fk or one-to-one relations
@@ -741,10 +732,6 @@ class ModelSerializer(Serializer):
if isinstance(self.fields.get(field_name, None), Serializer):
nested_forward_relations[field_name] = attrs[field_name]
- # Deleted related objects
- if self._deleted:
- removed_relations = list(self._deleted)
-
# Update an existing instance...
if instance is not None:
for key, val in attrs.items():
@@ -761,7 +748,6 @@ class ModelSerializer(Serializer):
instance._related_data = related_data
instance._m2m_data = m2m_data
instance._nested_forward_relations = nested_forward_relations
- instance._removed_relations = removed_relations
return instance
@@ -786,9 +772,6 @@ class ModelSerializer(Serializer):
obj.save(**kwargs)
- if self.allow_add_remove and hasattr(obj, '_removed_relations'):
- [self.delete_object(item) for item in obj._removed_relations]
-
if getattr(obj, '_m2m_data', None):
for accessor_name, object_list in obj._m2m_data.items():
setattr(obj, accessor_name, object_list)
@@ -804,6 +787,11 @@ class ModelSerializer(Serializer):
fk_field = obj._meta.get_field_by_name(accessor_name)[0].field.name
setattr(related_item, fk_field, obj)
self.save_object(related_item)
+
+ # Delete any removed objects
+ if field.allow_add_remove and related._deleted:
+ [self.delete_object(item) for item in related._deleted]
+
else:
# Nested reverse one-one relationship
fk_field = obj._meta.get_field_by_name(accessor_name)[0].field.name
diff --git a/rest_framework/tests/relations_nested.py b/rest_framework/tests/relations_nested.py
index 20683d4a..22c98e7f 100644
--- a/rest_framework/tests/relations_nested.py
+++ b/rest_framework/tests/relations_nested.py
@@ -287,3 +287,22 @@ class ReverseNestedOneToManyTests(TestCase):
]
self.assertEqual(serializer.data, expected)
+
+ def test_one_to_many_delete(self):
+ data = {'id': 1, 'name': 'target-1', 'sources': [{'id': 1, 'name': 'source-1'},
+ {'id': 3, 'name': 'source-3'}]}
+ instance = OneToManyTarget.objects.get(pk=1)
+ serializer = self.Serializer(instance, data=data)
+ self.assertTrue(serializer.is_valid())
+ serializer.save()
+
+ # Ensure source 2 is deleted, and everything else is as
+ # expected.
+ queryset = OneToManyTarget.objects.all()
+ serializer = self.Serializer(queryset)
+ expected = [
+ {'id': 1, 'name': 'target-1', 'sources': [{'id': 1, 'name': 'source-1'},
+ {'id': 3, 'name': 'source-3'}]}
+
+ ]
+ self.assertEqual(serializer.data, expected)