Skip to content

Commit 589b5dc

Browse files
pchiquetPierre Chiquet
and
Pierre Chiquet
authored
Allow to override child.run_validation call in ListSerializer (encode#8035)
* Separated run_child_validation method in ListSerializer * fix typo * Add test_update_allow_custom_child_validation --------- Co-authored-by: Pierre Chiquet <pierre.chiquet@ubikey.fr>
1 parent b99df0c commit 589b5dc

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

rest_framework/serializers.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,17 @@ def run_validation(self, data=empty):
653653

654654
return value
655655

656+
def run_child_validation(self, data):
657+
"""
658+
Run validation on child serializer.
659+
You may need to override this method to support multiple updates. For example:
660+
661+
self.child.instance = self.instance.get(pk=data['id'])
662+
self.child.initial_data = data
663+
return super().run_child_validation(data)
664+
"""
665+
return self.child.run_validation(data)
666+
656667
def to_internal_value(self, data):
657668
"""
658669
List of dicts of native values <- List of dicts of primitive datatypes.
@@ -697,7 +708,7 @@ def to_internal_value(self, data):
697708
):
698709
self.child.instance = self.instance[idx]
699710
try:
700-
validated = self.child.run_validation(item)
711+
validated = self.run_child_validation(item)
701712
except ValidationError as exc:
702713
errors.append(exc.detail)
703714
else:

tests/test_serializer_lists.py

+56-1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,61 @@ def test_validate_html_input(self):
153153
assert serializer.is_valid()
154154
assert serializer.validated_data == expected_output
155155

156+
def test_update_allow_custom_child_validation(self):
157+
"""
158+
Update a list of objects thanks custom run_child_validation implementation.
159+
"""
160+
161+
class TestUpdateSerializer(serializers.Serializer):
162+
integer = serializers.IntegerField()
163+
boolean = serializers.BooleanField()
164+
165+
def update(self, instance, validated_data):
166+
instance._data.update(validated_data)
167+
return instance
168+
169+
def validate(self, data):
170+
# self.instance is set to current BasicObject instance
171+
assert isinstance(self.instance, BasicObject)
172+
# self.initial_data is current dictionary
173+
assert isinstance(self.initial_data, dict)
174+
assert self.initial_data["pk"] == self.instance.pk
175+
return super().validate(data)
176+
177+
class ListUpdateSerializer(serializers.ListSerializer):
178+
child = TestUpdateSerializer()
179+
180+
def run_child_validation(self, data):
181+
# find related instance in self.instance list
182+
child_instance = next(o for o in self.instance if o.pk == data["pk"])
183+
# set instance and initial_data for child serializer
184+
self.child.instance = child_instance
185+
self.child.initial_data = data
186+
return super().run_child_validation(data)
187+
188+
def update(self, instance, validated_data):
189+
return [
190+
self.child.update(instance, attrs)
191+
for instance, attrs in zip(self.instance, validated_data)
192+
]
193+
194+
instance = [
195+
BasicObject(pk=1, integer=11, private_field="a"),
196+
BasicObject(pk=2, integer=22, private_field="b"),
197+
]
198+
input_data = [
199+
{"pk": 1, "integer": "123", "boolean": "true"},
200+
{"pk": 2, "integer": "456", "boolean": "false"},
201+
]
202+
expected_output = [
203+
BasicObject(pk=1, integer=123, boolean=True, private_field="a"),
204+
BasicObject(pk=2, integer=456, boolean=False, private_field="b"),
205+
]
206+
serializer = ListUpdateSerializer(instance, data=input_data)
207+
assert serializer.is_valid()
208+
updated_instances = serializer.save()
209+
assert updated_instances == expected_output
210+
156211

157212
class TestNestedListSerializer:
158213
"""
@@ -481,7 +536,7 @@ class Serializer(serializers.Serializer):
481536
assert serializer.validated_data == {}
482537
assert serializer.errors == {}
483538

484-
def test_udate_as_field_allow_empty_true(self):
539+
def test_update_as_field_allow_empty_true(self):
485540
class ListSerializer(serializers.Serializer):
486541
update_field = serializers.IntegerField()
487542
store_field = serializers.IntegerField()

0 commit comments

Comments
 (0)