From f31b2ffa5c27e4c7f5c2c78a156039bae8d5d874 Mon Sep 17 00:00:00 2001 From: justyou0606 Date: Sun, 14 Jun 2026 10:27:39 +0800 Subject: [PATCH] Fix duplicate validation errors for GenericIPAddressField with protocol When a Django model uses GenericIPAddressField(protocol='IPv4') or (protocol='IPv6'), the model field gets a protocol-specific validator (validate_ipv4_address or validate_ipv6_address) from Django. DRF's IPAddressField also adds the same validator via ip_address_validators(), resulting in duplicate error messages. Fix: Remove validate_ipv4_address and validate_ipv6_address from the serializer's validator_kwarg, in addition to the already-removed validate_ipv46_address. Fixes #9645 --- rest_framework/utils/field_mapping.py | 6 ++++- tests/test_model_serializer.py | 36 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index d35caca0c7..6637ce366b 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -208,7 +208,11 @@ def get_field_kwargs(field_name, model_field): if isinstance(model_field, models.GenericIPAddressField): validator_kwarg = [ validator for validator in validator_kwarg - if validator is not validators.validate_ipv46_address + if validator not in ( + validators.validate_ipv46_address, + validators.validate_ipv4_address, + validators.validate_ipv6_address, + ) ] # Our decimal validation is handled in the field code, not validator code. if isinstance(model_field, models.DecimalField): diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 0e291a2de8..abb86b78cc 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -439,6 +439,42 @@ class Meta: 'Unexpected number of validation errors: ' '{}'.format(s.errors)) + def test_ip_address_validation_with_protocol_ipv4(self): + class IPAddressFieldModel(models.Model): + address = models.GenericIPAddressField(protocol='IPv4') + + class Meta: + app_label = 'test_model_serializer' + + class TestSerializer(serializers.ModelSerializer): + class Meta: + model = IPAddressFieldModel + fields = '__all__' + + s = TestSerializer(data={'address': 'not an ip address'}) + self.assertFalse(s.is_valid()) + self.assertEqual(1, len(s.errors['address']), + 'Unexpected number of validation errors: ' + '{}'.format(s.errors)) + + def test_ip_address_validation_with_protocol_ipv6(self): + class IPAddressFieldModel(models.Model): + address = models.GenericIPAddressField(protocol='IPv6') + + class Meta: + app_label = 'test_model_serializer' + + class TestSerializer(serializers.ModelSerializer): + class Meta: + model = IPAddressFieldModel + fields = '__all__' + + s = TestSerializer(data={'address': 'not an ip address'}) + self.assertFalse(s.is_valid()) + self.assertEqual(1, len(s.errors['address']), + 'Unexpected number of validation errors: ' + '{}'.format(s.errors)) + @pytest.mark.skipif('not postgres_fields') class TestPosgresFieldsMapping(TestCase):