From cf09681a7352504f9970ad5df4dbc49087410479 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 10 Apr 2025 22:07:02 -0500 Subject: [PATCH 1/3] Add failing test for `UniqueConstraint` validation with `source` attribute --- tests/test_validators.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_validators.py b/tests/test_validators.py index 29b097ef39..d19734d98c 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -683,6 +683,24 @@ def test_nullable_unique_constraint_fields_are_not_required(self): result = serializer.save() self.assertIsInstance(result, UniqueConstraintNullableModel) + def test_unique_constraint_source(self): + class SourceUniqueConstraintSerializer(serializers.ModelSerializer): + raceName = serializers.CharField(source="race_name") + + class Meta: + model = UniqueConstraintModel + fields = ("raceName", "position", "global_id", "fancy_conditions") + + serializer = SourceUniqueConstraintSerializer( + data={ + "raceName": "example", + "position": 5, + "global_id": 11, + "fancy_conditions": 11, + } + ) + assert serializer.is_valid() + # Tests for `UniqueForDateValidator` # ---------------------------------- From 2acdc6c51dfff4dc2718b1a2dc56390d92beacc1 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 14 Apr 2025 11:54:04 -0500 Subject: [PATCH 2/3] Fix `UniqueTogetherValidator` to handle fields with source attribute --- rest_framework/validators.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rest_framework/validators.py b/rest_framework/validators.py index a152c6362f..8006e8788e 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -188,7 +188,10 @@ def __call__(self, attrs, serializer): if attrs[field_name] != getattr(serializer.instance, field_name) ] - condition_kwargs = {source: attrs[source] for source in self.condition_fields} + condition_kwargs = { + serializer.fields[field_name].source: attrs[serializer.fields[field_name].source] + for field_name in self.condition_fields + } 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) From 4a3cba4a2b701e883ca7387dece19b7247588e07 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 22 Apr 2025 08:32:58 -0500 Subject: [PATCH 3/3] split inner sources logic out to tuple comprehension --- rest_framework/validators.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 8006e8788e..4c444cf01e 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -188,10 +188,8 @@ def __call__(self, attrs, serializer): if attrs[field_name] != getattr(serializer.instance, field_name) ] - condition_kwargs = { - serializer.fields[field_name].source: attrs[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} 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)