diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 01bebf5fc..fb2a5bd1e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -589,6 +589,11 @@ class ListSerializer(BaseSerializer): self.min_length = kwargs.pop('min_length', None) assert self.child is not None, '`child` is a required argument.' assert not inspect.isclass(self.child), '`child` has not been instantiated.' + + if kwargs.get('instance', []) and kwargs.get('data', []): + assert len(kwargs.get("data", [])) == len( + kwargs.get("instance", [])), 'Data and instance should have same length' + super().__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) @@ -663,7 +668,10 @@ class ListSerializer(BaseSerializer): ret = [] errors = [] - for item in data: + for idx, item in enumerate(data): + if hasattr(self, 'instance') and self.instance and \ + len(self.instance) > idx: + self.child.instance = self.instance[idx] try: validated = self.child.run_validation(item) except ValidationError as exc: diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 1d9efaa43..0f57ba6d2 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -762,3 +762,66 @@ class Test8301Regression: assert (s.data | {}).__class__ == s.data.__class__ assert ({} | s.data).__class__ == s.data.__class__ + + +class MyClass(models.Model): + name = models.CharField(max_length=100) + value = models.CharField(max_length=100, blank=True) + + app_label = "test" + + @property + def is_valid(self): + return self.name == 'valid' + + +class MyClassSerializer(serializers.ModelSerializer): + class Meta: + model = MyClass + fields = ('id', 'name', 'value') + + def validate_value(self, value): + if value and not self.instance.is_valid: + raise serializers.ValidationError( + 'Status cannot be set for invalid instance') + return value + + +import unittest + + +class TestMultipleObjectsValidation(unittest.TestCase): + def setUp(self): + self.objs = [ + MyClass(name='valid'), + MyClass(name='invalid'), + MyClass(name='other'), + ] + + def test_multiple_objects_are_validated_separately(self): + + serializer = MyClassSerializer( + data=[{'value': 'set', 'id': instance.id} for instance in + self.objs], + instance=self.objs, + many=True, + partial=True, + ) + + assert not serializer.is_valid() + assert serializer.errors == [ + {}, + {'value': ['Status cannot be set for invalid instance']}, + {'value': ['Status cannot be set for invalid instance']} + ] + + def test_exception_raised_when_data_and_instance_length_different(self): + + with self.assertRaises(AssertionError): + serializer = MyClassSerializer( + data=[{'value': 'set', 'id': instance.id} for instance in + self.objs], + instance=self.objs[:-1], + many=True, + partial=True, + ) \ No newline at end of file