Skip to content

Allow to override child.run_validation call in ListSerializer #8035

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion rest_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,17 @@ def run_validation(self, data=empty):

return value

def run_child_validation(self, data):
"""
Run validation on child serializer.
You may need to override this method to support multiple updates. For example:

self.child.instance = self.instance.get(pk=data['id'])
self.child.initial_data = data
return super().run_child_validation(data)
"""
return self.child.run_validation(data)

def to_internal_value(self, data):
"""
List of dicts of native values <- List of dicts of primitive datatypes.
Expand Down Expand Up @@ -665,7 +676,7 @@ def to_internal_value(self, data):

for item in data:
try:
validated = self.child.run_validation(item)
validated = self.run_child_validation(item)
except ValidationError as exc:
errors.append(exc.detail)
else:
Expand Down
57 changes: 56 additions & 1 deletion tests/test_serializer_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,61 @@ def test_validate_html_input(self):
assert serializer.is_valid()
assert serializer.validated_data == expected_output

def test_update_allow_custom_child_validation(self):
"""
Update a list of objects thanks custom run_child_validation implementation.
"""

class TestUpdateSerializer(serializers.Serializer):
integer = serializers.IntegerField()
boolean = serializers.BooleanField()

def update(self, instance, validated_data):
instance._data.update(validated_data)
return instance

def validate(self, data):
# self.instance is set to current BasicObject instance
assert isinstance(self.instance, BasicObject)
# self.initial_data is current dictionary
assert isinstance(self.initial_data, dict)
assert self.initial_data["pk"] == self.instance.pk
return super().validate(data)

class ListUpdateSerializer(serializers.ListSerializer):
child = TestUpdateSerializer()

def run_child_validation(self, data):
# find related instance in self.instance list
child_instance = next(o for o in self.instance if o.pk == data["pk"])
# set instance and initial_data for child serializer
self.child.instance = child_instance
self.child.initial_data = data
return super().run_child_validation(data)

def update(self, instance, validated_data):
return [
self.child.update(instance, attrs)
for instance, attrs in zip(self.instance, validated_data)
]

instance = [
BasicObject(pk=1, integer=11, private_field="a"),
BasicObject(pk=2, integer=22, private_field="b"),
]
input_data = [
{"pk": 1, "integer": "123", "boolean": "true"},
{"pk": 2, "integer": "456", "boolean": "false"},
]
expected_output = [
BasicObject(pk=1, integer=123, boolean=True, private_field="a"),
BasicObject(pk=2, integer=456, boolean=False, private_field="b"),
]
serializer = ListUpdateSerializer(instance, data=input_data)
assert serializer.is_valid()
updated_instances = serializer.save()
assert updated_instances == expected_output


class TestNestedListSerializer:
"""
Expand Down Expand Up @@ -481,7 +536,7 @@ class Serializer(serializers.Serializer):
assert serializer.validated_data == {}
assert serializer.errors == {}

def test_udate_as_field_allow_empty_true(self):
def test_update_as_field_allow_empty_true(self):
class ListSerializer(serializers.Serializer):
update_field = serializers.IntegerField()
store_field = serializers.IntegerField()
Expand Down