From e454758fb6edf1dcf5aa5417a388b940c871469c Mon Sep 17 00:00:00 2001 From: Konstantin Alekseev Date: Tue, 10 Jun 2025 14:47:28 +0300 Subject: [PATCH] Fix regression in unique_together validation with SerializerMethodField (#9712) --- rest_framework/serializers.py | 3 ++- tests/test_validators.py | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 0b87aa8fc..8fe284bc8 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1469,12 +1469,13 @@ class ModelSerializer(Serializer): model_field.unique_for_year} unique_constraint_names -= {None} + model_fields_names = set(model_fields.keys()) # Include each of the `unique_together` and `UniqueConstraint` field names, # so long as all the field names are included on the serializer. for unique_together_list, queryset, condition_fields, condition in self.get_unique_together_constraints(model): unique_together_list_and_condition_fields = set(unique_together_list) | set(condition_fields) - if set(field_names).issuperset(unique_together_list_and_condition_fields): + if model_fields_names.issuperset(unique_together_list_and_condition_fields): unique_constraint_names |= unique_together_list_and_condition_fields # Now we have all the field names that have uniqueness constraints diff --git a/tests/test_validators.py b/tests/test_validators.py index d19734d98..c594eecbe 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -516,6 +516,43 @@ class TestUniquenessTogetherValidation(TestCase): validator.filter_queryset(attrs=data, queryset=queryset, serializer=serializer) assert queryset.called_with == {'race_name': 'bar', 'position': 1} + def test_uniq_together_validation_uses_model_fields_method_field(self): + class TestSerializer(serializers.ModelSerializer): + position = serializers.SerializerMethodField() + + def get_position(self, obj): + return obj.position or 0 + + class Meta: + model = NullUniquenessTogetherModel + fields = ['race_name', 'position'] + + serializer = TestSerializer() + expected = dedent(""" + TestSerializer(): + race_name = CharField(max_length=100) + position = SerializerMethodField() + """) + assert repr(serializer) == expected + + def test_uniq_together_validation_uses_model_fields_with_source_field(self): + class TestSerializer(serializers.ModelSerializer): + pos = serializers.IntegerField(source='position') + + class Meta: + model = NullUniquenessTogetherModel + fields = ['race_name', 'pos'] + + serializer = TestSerializer() + expected = dedent(""" + TestSerializer(): + race_name = CharField(max_length=100, required=True) + pos = IntegerField(source='position') + class Meta: + validators = [] + """) + assert repr(serializer) == expected + class UniqueConstraintModel(models.Model): race_name = models.CharField(max_length=100)