From 513ddb4ffbfaef9d0117b281121ffc7319106f2f Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 13 Aug 2025 06:53:25 +0200 Subject: [PATCH] Condition of UniqueTogetherValidator can be read-only (#9764) * Condition of UniqueValidator can be read-only We can't always expect to find the value of the condition in the serializer if the field is read-only. * Reproducible test --- rest_framework/validators.py | 7 ++++++- tests/test_validators.py | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 4c444cf01..76d2a2159 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -189,7 +189,12 @@ class UniqueTogetherValidator: ] condition_sources = (serializer.fields[field_name].source for field_name in self.condition_fields) - condition_kwargs = {source: attrs[source] for source in condition_sources} + condition_kwargs = { + source: attrs[source] + if source in attrs + else getattr(serializer.instance, source) + for source in condition_sources + } if checked_values and None not in checked_values and qs_exists_with_condition(queryset, self.condition, condition_kwargs): field_names = ', '.join(self.fields) message = self.message.format(field_names=field_names) diff --git a/tests/test_validators.py b/tests/test_validators.py index c594eecbe..ea5bf3a4d 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -589,6 +589,21 @@ class UniqueConstraintModel(models.Model): ] +class UniqueConstraintReadOnlyFieldModel(models.Model): + state = models.CharField(max_length=100, default="new") + position = models.IntegerField() + something = models.IntegerField() + + class Meta: + constraints = [ + models.UniqueConstraint( + name="unique_constraint_%(class)s", + fields=("position", "something"), + condition=models.Q(state="new"), + ), + ] + + class UniqueConstraintNullableModel(models.Model): title = models.CharField(max_length=100) age = models.IntegerField(null=True) @@ -738,6 +753,31 @@ class TestUniqueConstraintValidation(TestCase): ) assert serializer.is_valid() + def test_uniq_constraint_condition_read_only_create(self): + class UniqueConstraintReadOnlyFieldModelSerializer(serializers.ModelSerializer): + class Meta: + model = UniqueConstraintReadOnlyFieldModel + read_only_fields = ("state",) + fields = ("position", "something", *read_only_fields) + serializer = UniqueConstraintReadOnlyFieldModelSerializer( + data={"position": 1, "something": 1} + ) + assert serializer.is_valid() + + def test_uniq_constraint_condition_read_only_partial(self): + class UniqueConstraintReadOnlyFieldModelSerializer(serializers.ModelSerializer): + class Meta: + model = UniqueConstraintReadOnlyFieldModel + read_only_fields = ("state",) + fields = ("position", "something", *read_only_fields) + instance = UniqueConstraintReadOnlyFieldModel.objects.create(position=1, something=1) + serializer = UniqueConstraintReadOnlyFieldModelSerializer( + instance=instance, + data={"position": 1, "something": 1}, + partial=True + ) + assert serializer.is_valid() + # Tests for `UniqueForDateValidator` # ----------------------------------