mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-09-16 01:02:29 +03:00
Merge 7ef2f1d368
into 0f576223f2
This commit is contained in:
commit
80294b0b41
|
@ -652,6 +652,52 @@ class ListSerializer(BaseSerializer):
|
||||||
self.child.initial_data = data
|
self.child.initial_data = data
|
||||||
return super().run_child_validation(data)
|
return super().run_child_validation(data)
|
||||||
"""
|
"""
|
||||||
|
child_instance = getattr(self.child, "instance", None)
|
||||||
|
|
||||||
|
if self.instance is not None:
|
||||||
|
pk_name = None
|
||||||
|
child_meta = getattr(self.child, "Meta", None)
|
||||||
|
model = getattr(child_meta, "model", None) if child_meta else None
|
||||||
|
|
||||||
|
if model is not None:
|
||||||
|
pk_name = model._meta.pk.name
|
||||||
|
|
||||||
|
obj_id = None
|
||||||
|
if pk_name:
|
||||||
|
for field_name, field in self.child.fields.items():
|
||||||
|
if getattr(field, "source", None) == pk_name:
|
||||||
|
obj_id = data.get(field_name)
|
||||||
|
if obj_id is not None:
|
||||||
|
break
|
||||||
|
|
||||||
|
if obj_id is None:
|
||||||
|
obj_id = data.get(pk_name) or data.get("pk") or data.get("id")
|
||||||
|
|
||||||
|
resolved_instance = None
|
||||||
|
|
||||||
|
if obj_id is not None and pk_name:
|
||||||
|
try:
|
||||||
|
obj_id = model._meta.pk.to_python(obj_id)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not hasattr(self, "_instance_index"):
|
||||||
|
self._instance_index = {
|
||||||
|
getattr(obj, pk_name): obj for obj in self.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved_instance = self._instance_index.get(obj_id)
|
||||||
|
|
||||||
|
if resolved_instance is None:
|
||||||
|
if model is not None and self.context.get("allow_create", True):
|
||||||
|
resolved_instance = model()
|
||||||
|
else:
|
||||||
|
resolved_instance = child_instance
|
||||||
|
|
||||||
|
child_instance = resolved_instance
|
||||||
|
|
||||||
|
self.child.instance = child_instance
|
||||||
|
self.child.initial_data = data
|
||||||
return self.child.run_validation(data)
|
return self.child.run_validation(data)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
|
|
|
@ -150,3 +150,30 @@ class CustomManagerModel(RESTFrameworkModel):
|
||||||
help_text='OneToOneTarget',
|
help_text='OneToOneTarget',
|
||||||
verbose_name='OneToOneTarget',
|
verbose_name='OneToOneTarget',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
|
||||||
|
class ListModelForTest(RESTFrameworkModel):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
status = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_valid(self):
|
||||||
|
return self.name == 'valid'
|
||||||
|
|
||||||
|
|
||||||
|
class EmailPKModel(RESTFrameworkModel):
|
||||||
|
email = models.EmailField(primary_key=True)
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_valid(self):
|
||||||
|
return self.name == 'valid'
|
||||||
|
|
||||||
|
|
||||||
|
class PersonUUID(RESTFrameworkModel):
|
||||||
|
id = models.UUIDField(primary_key=True)
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_valid(self):
|
||||||
|
return self.name == 'valid'
|
||||||
|
|
|
@ -5,7 +5,8 @@ from django.utils.datastructures import MultiValueDict
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.exceptions import ErrorDetail
|
from rest_framework.exceptions import ErrorDetail
|
||||||
from tests.models import (
|
from tests.models import (
|
||||||
CustomManagerModel, NullableOneToOneSource, OneToOneTarget
|
CustomManagerModel, EmailPKModel, ListModelForTest, NullableOneToOneSource,
|
||||||
|
OneToOneTarget, PersonUUID
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -775,3 +776,102 @@ class TestToRepresentationManagerCheck:
|
||||||
queryset = NullableOneToOneSource.objects.all()
|
queryset = NullableOneToOneSource.objects.all()
|
||||||
serializer = self.serializer(queryset, many=True)
|
serializer = self.serializer(queryset, many=True)
|
||||||
assert serializer.data
|
assert serializer.data
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class TestManyTrueValidationCheck:
|
||||||
|
"""
|
||||||
|
Tests ListSerializer validation with many=True across different primary key types
|
||||||
|
(integer and email).
|
||||||
|
"""
|
||||||
|
|
||||||
|
class PersonUUIDSerializer(serializers.ModelSerializer):
|
||||||
|
uuid = serializers.UUIDField(source="id")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = PersonUUID
|
||||||
|
fields = ("uuid", "name")
|
||||||
|
read_only_fields = ("uuid",)
|
||||||
|
|
||||||
|
def validate_name(self, value):
|
||||||
|
if value and not self.instance.is_valid:
|
||||||
|
return False
|
||||||
|
return value
|
||||||
|
|
||||||
|
def setup_method(self):
|
||||||
|
self.obj1 = ListModelForTest.objects.create(name="valid", status="new")
|
||||||
|
self.obj2 = ListModelForTest.objects.create(name="invalid", status="")
|
||||||
|
self.email_obj1 = EmailPKModel.objects.create(email="test@test.com", name="A")
|
||||||
|
self.email_obj2 = EmailPKModel.objects.create(email="test2@test.com", name="B")
|
||||||
|
|
||||||
|
self.serializer, self.email_serializer = self.get_serializers()
|
||||||
|
|
||||||
|
def get_serializers(self):
|
||||||
|
class ListModelForTestSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = ListModelForTest
|
||||||
|
fields = ("id", "name", "status")
|
||||||
|
|
||||||
|
def validate_status(self, value):
|
||||||
|
if value and not self.instance.is_valid:
|
||||||
|
return False
|
||||||
|
return value
|
||||||
|
|
||||||
|
class EmailPKSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = EmailPKModel
|
||||||
|
fields = ("email", "name")
|
||||||
|
read_only_fields = ('email',)
|
||||||
|
|
||||||
|
def validate_name(self, value):
|
||||||
|
if value and not self.instance.is_valid:
|
||||||
|
return False
|
||||||
|
return value
|
||||||
|
|
||||||
|
return ListModelForTestSerializer, EmailPKSerializer
|
||||||
|
|
||||||
|
def test_run_child_validation_with_many_true(self):
|
||||||
|
input_data = [
|
||||||
|
{"id": self.obj1.pk, "name": "other", "status": "new"},
|
||||||
|
{"id": self.obj2.pk, "name": "valid", "status": "progress"},
|
||||||
|
]
|
||||||
|
|
||||||
|
serializer = self.serializer([self.obj1, self.obj2], data=input_data, many=True)
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
serializer = self.serializer(ListModelForTest.objects.all(), data=input_data, many=True)
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
def test_validation_error_for_invalid_data(self):
|
||||||
|
input_data = [{"id": self.obj1.pk, "name": "", "status": "mystatus"}]
|
||||||
|
|
||||||
|
serializer = self.serializer([self.obj1], data=input_data, many=True)
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert "name" in serializer.errors[0]
|
||||||
|
|
||||||
|
def test_email_pk_instance_validation(self):
|
||||||
|
input_data = [{"email": "test@test.com", "name": "bar"}]
|
||||||
|
serializer = self.email_serializer(instance=EmailPKModel.objects.all(), data=input_data, many=True)
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
def test_uuid_validate_many(self):
|
||||||
|
PersonUUID.objects.create(id="c20f2f31-65a3-451f-ae7d-e939b7d9f84b", name="valid")
|
||||||
|
PersonUUID.objects.create(id="3308237e-18d8-4074-9d05-79cc0fdb5bb3", name="other")
|
||||||
|
|
||||||
|
input_data = [
|
||||||
|
{
|
||||||
|
"uuid": "c20f2f31-65a3-451f-ae7d-e939b7d9f84b",
|
||||||
|
"name": "bar",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
serializer = self.PersonUUIDSerializer(instance=list(PersonUUID.objects.all()), data=input_data, many=True)
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
def test_uuid_validate_single(self):
|
||||||
|
instance = PersonUUID.objects.create(id="c20f2f31-65a3-451f-ae7d-e939b7d9f84b", name="food")
|
||||||
|
|
||||||
|
serializer = self.PersonUUIDSerializer(
|
||||||
|
instance=instance,
|
||||||
|
data={"uuid": "c20f2f31-65a3-451f-ae7d-e939b7d9f84b", "name": "valid"},
|
||||||
|
)
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user