fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer

This commit is contained in:
Saadullah Aleem 2023-05-11 01:19:12 +05:00
parent 99e8b4033e
commit a717d74f3c
2 changed files with 72 additions and 1 deletions

View File

@ -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:

View File

@ -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,
)