diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 07ad11b47b..3f09c15cd6 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -160,10 +160,19 @@ def __call__(self, attrs, serializer): queryset = self.exclude_current_instance(attrs, queryset, serializer.instance) # Ignore validation if any field is None - checked_values = [ - value for field, value in attrs.items() if field in self.fields - ] - if None not in checked_values and qs_exists(queryset): + if serializer.instance is None: + checked_values = [ + value for field, value in attrs.items() if field in self.fields + ] + else: + # Ignore validation if all field values are unchanged + checked_values = [ + value + for field, value in attrs.items() + if field in self.fields and value != getattr(serializer.instance, field) + ] + + if checked_values and None not in checked_values and qs_exists(queryset): field_names = ', '.join(self.fields) message = self.message.format(field_names=field_names) raise ValidationError(message, code='unique') diff --git a/tests/test_validators.py b/tests/test_validators.py index 1cf42ed07e..49b0db63a2 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -1,5 +1,5 @@ import datetime -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import pytest from django.db import DataError, models @@ -447,6 +447,22 @@ def test_do_not_ignore_validation_for_null_fields(self): serializer = NullUniquenessTogetherSerializer(data=data) assert not serializer.is_valid() + def test_ignore_validation_for_unchanged_fields(self): + """ + If all fields in the unique together constraint are unchanged, + then the instance should skip uniqueness validation. + """ + instance = UniquenessTogetherModel.objects.create( + race_name="Paris Marathon", position=1 + ) + data = {"race_name": "Paris Marathon", "position": 1} + serializer = UniquenessTogetherSerializer(data=data, instance=instance) + with patch( + "rest_framework.validators.qs_exists" + ) as mock: + assert serializer.is_valid() + assert not mock.called + def test_filter_queryset_do_not_skip_existing_attribute(self): """ filter_queryset should add value from existing instance attribute pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy