Skip to content

Commit 06ebf29

Browse files
committed
updated child validation for ListSerializer
1 parent 1472848 commit 06ebf29

File tree

3 files changed

+104
-1
lines changed

3 files changed

+104
-1
lines changed

rest_framework/serializers.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,26 @@ def run_child_validation(self, data):
652652
self.child.initial_data = data
653653
return super().run_child_validation(data)
654654
"""
655+
child_instance = getattr(self.child, "instance", None)
656+
657+
if self.instance is not None:
658+
pk_name = None
659+
child_meta = getattr(self.child, "Meta", None)
660+
model = getattr(child_meta, "model", None) if child_meta else None
661+
662+
if model is not None:
663+
pk_name = model._meta.pk.name
664+
665+
if pk_name:
666+
obj_id = data.get(pk_name, data.get("pk", data.get("id")))
667+
if obj_id is not None:
668+
for obj in self.instance:
669+
if hasattr(obj, pk_name) and getattr(obj, pk_name) == obj_id:
670+
child_instance = obj
671+
break
672+
673+
self.child.instance = child_instance
674+
self.child.initial_data = data
655675
return self.child.run_validation(data)
656676

657677
def to_internal_value(self, data):

tests/models.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,21 @@ def __new__(cls, *args, **kwargs):
150150
help_text='OneToOneTarget',
151151
verbose_name='OneToOneTarget',
152152
on_delete=models.CASCADE)
153+
154+
155+
class ListModelForTest(RESTFrameworkModel):
156+
name = models.CharField(max_length=100)
157+
status = models.CharField(max_length=100, blank=True)
158+
159+
@property
160+
def is_valid(self):
161+
return self.name == 'valid'
162+
163+
164+
class EmailPKModel(RESTFrameworkModel):
165+
email = models.EmailField(primary_key=True)
166+
name = models.CharField(max_length=100)
167+
168+
@property
169+
def is_valid(self):
170+
return self.name == 'valid'

tests/test_serializer_lists.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from rest_framework import serializers
66
from rest_framework.exceptions import ErrorDetail
77
from tests.models import (
8-
CustomManagerModel, NullableOneToOneSource, OneToOneTarget
8+
CustomManagerModel, EmailPKModel, ListModelForTest, NullableOneToOneSource,
9+
OneToOneTarget
910
)
1011

1112

@@ -775,3 +776,67 @@ def test(self):
775776
queryset = NullableOneToOneSource.objects.all()
776777
serializer = self.serializer(queryset, many=True)
777778
assert serializer.data
779+
780+
781+
@pytest.mark.django_db
782+
class TestManyTrueValidationCheck:
783+
"""
784+
Tests ListSerializer validation with many=True across different primary key types
785+
(integer, UUID, and email).
786+
"""
787+
788+
def setup_method(self):
789+
self.obj1 = ListModelForTest.objects.create(name="valid", status="new")
790+
self.obj2 = ListModelForTest.objects.create(name="invalid", status="")
791+
self.email_obj1 = EmailPKModel.objects.create(email="[email protected]", name="A")
792+
self.email_obj2 = EmailPKModel.objects.create(email="[email protected]", name="B")
793+
794+
self.serializer, self.email_serializer = self.get_serializers()
795+
796+
def get_serializers(self):
797+
class ListModelForTestSerializer(serializers.ModelSerializer):
798+
class Meta:
799+
model = ListModelForTest
800+
fields = ("id", "name", "status")
801+
802+
def validate_status(self, value):
803+
if value and not self.instance.is_valid:
804+
return False
805+
return value
806+
807+
class EmailPKSerializer(serializers.ModelSerializer):
808+
class Meta:
809+
model = EmailPKModel
810+
fields = ("email", "name")
811+
read_only_fields = ('email',)
812+
813+
def validate_name(self, value):
814+
if value and not self.instance.is_valid:
815+
return False
816+
return value
817+
818+
return ListModelForTestSerializer, EmailPKSerializer
819+
820+
def test_run_child_validation_with_many_true(self):
821+
input_data = [
822+
{"id": self.obj1.pk, "name": "other", "status": "new"},
823+
{"id": self.obj2.pk, "name": "valid", "status": "progress"},
824+
]
825+
826+
serializer = self.serializer([self.obj1, self.obj2], data=input_data, many=True)
827+
assert serializer.is_valid(), serializer.errors
828+
829+
serializer = self.serializer(ListModelForTest.objects.all(), data=input_data, many=True)
830+
assert serializer.is_valid(), serializer.errors
831+
832+
def test_validation_error_for_invalid_data(self):
833+
input_data = [{"id": self.obj1.pk, "name": "", "status": "mystatus"}]
834+
835+
serializer = self.serializer([self.obj1], data=input_data, many=True)
836+
assert not serializer.is_valid()
837+
assert "name" in serializer.errors[0]
838+
839+
def test_email_pk_instance_validation(self):
840+
input_data = [{"email": "[email protected]", "name": "bar"}]
841+
serializer = self.email_serializer(instance=EmailPKModel.objects.all(), data=input_data, many=True)
842+
assert serializer.is_valid(), serializer.errors

0 commit comments

Comments
 (0)