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
This commit is contained in:
Nicolas Delaby 2025-08-13 06:53:25 +02:00 committed by GitHub
parent c8b6d3dcdf
commit 513ddb4ffb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 1 deletions

View File

@ -189,7 +189,12 @@ class UniqueTogetherValidator:
] ]
condition_sources = (serializer.fields[field_name].source for field_name in self.condition_fields) 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): if checked_values and None not in checked_values and qs_exists_with_condition(queryset, self.condition, condition_kwargs):
field_names = ', '.join(self.fields) field_names = ', '.join(self.fields)
message = self.message.format(field_names=field_names) message = self.message.format(field_names=field_names)

View File

@ -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): class UniqueConstraintNullableModel(models.Model):
title = models.CharField(max_length=100) title = models.CharField(max_length=100)
age = models.IntegerField(null=True) age = models.IntegerField(null=True)
@ -738,6 +753,31 @@ class TestUniqueConstraintValidation(TestCase):
) )
assert serializer.is_valid() 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` # Tests for `UniqueForDateValidator`
# ---------------------------------- # ----------------------------------