diff --git a/django/core/validators.py b/django/core/validators.py index 9b04dad4ab..57940a59da 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -276,14 +276,18 @@ def validate_ipv4_address(value): ipaddress.IPv4Address(value) except ValueError: raise ValidationError( - _("Enter a valid IPv4 address."), code="invalid", params={"value": value} + _("Enter a valid %(protocol)s address."), + code="invalid", + params={"protocol": _("IPv4"), "value": value}, ) def validate_ipv6_address(value): if not is_valid_ipv6_address(value): raise ValidationError( - _("Enter a valid IPv6 address."), code="invalid", params={"value": value} + _("Enter a valid %(protocol)s address."), + code="invalid", + params={"protocol": _("IPv6"), "value": value}, ) @@ -295,16 +299,16 @@ def validate_ipv46_address(value): validate_ipv6_address(value) except ValidationError: raise ValidationError( - _("Enter a valid IPv4 or IPv6 address."), + _("Enter a valid %(protocol)s address."), code="invalid", - params={"value": value}, + params={"protocol": _("IPv4 or IPv6"), "value": value}, ) ip_address_validator_map = { - "both": ([validate_ipv46_address], _("Enter a valid IPv4 or IPv6 address.")), - "ipv4": ([validate_ipv4_address], _("Enter a valid IPv4 address.")), - "ipv6": ([validate_ipv6_address], _("Enter a valid IPv6 address.")), + "both": [validate_ipv46_address], + "ipv4": [validate_ipv4_address], + "ipv6": [validate_ipv6_address], } diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index ce3bebb591..67e8ddc986 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -2201,7 +2201,7 @@ class IPAddressField(Field): class GenericIPAddressField(Field): empty_strings_allowed = False description = _("IP address") - default_error_messages = {} + default_error_messages = {"invalid": _("Enter a valid %(protocol)s address.")} def __init__( self, @@ -2214,11 +2214,9 @@ class GenericIPAddressField(Field): ): self.unpack_ipv4 = unpack_ipv4 self.protocol = protocol - ( - self.default_validators, - invalid_error_message, - ) = validators.ip_address_validators(protocol, unpack_ipv4) - self.default_error_messages["invalid"] = invalid_error_message + self.default_validators = validators.ip_address_validators( + protocol, unpack_ipv4 + ) kwargs["max_length"] = 39 super().__init__(verbose_name, name, *args, **kwargs) diff --git a/django/forms/fields.py b/django/forms/fields.py index 3a0510ffc9..d1ba8af654 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -1288,7 +1288,7 @@ class GenericIPAddressField(CharField): self.unpack_ipv4 = unpack_ipv4 self.default_validators = validators.ip_address_validators( protocol, unpack_ipv4 - )[0] + ) super().__init__(**kwargs) def to_python(self, value): diff --git a/django/utils/ipv6.py b/django/utils/ipv6.py index 88dd6ecb4b..8b691b5e66 100644 --- a/django/utils/ipv6.py +++ b/django/utils/ipv6.py @@ -26,7 +26,9 @@ def clean_ipv6_address( try: addr = ipaddress.IPv6Address(int(ipaddress.IPv6Address(ip_str))) except ValueError: - raise ValidationError(error_message, code="invalid") + raise ValidationError( + error_message, code="invalid", params={"protocol": _("IPv6")} + ) if unpack_ipv4 and addr.ipv4_mapped: return str(addr.ipv4_mapped) diff --git a/tests/validation/tests.py b/tests/validation/tests.py index 43de77a44a..6bb04f6f14 100644 --- a/tests/validation/tests.py +++ b/tests/validation/tests.py @@ -206,3 +206,17 @@ class GenericIPAddressFieldTests(ValidationAssertions, TestCase): self.assertIsNone(giptm.full_clean()) giptm = GenericIPAddressTestModel(generic_ip=None) self.assertIsNone(giptm.full_clean()) + + def test_multiple_invalid_ip_raises_error(self): + giptm = GenericIPAddressTestModel( + v6_ip="1.2.3.4", v4_ip="::ffff:10.10.10.10", generic_ip="fsad" + ) + self.assertFieldFailsValidationWithMessage( + giptm.full_clean, "v6_ip", ["Enter a valid IPv6 address."] + ) + self.assertFieldFailsValidationWithMessage( + giptm.full_clean, "v4_ip", ["Enter a valid IPv4 address."] + ) + self.assertFieldFailsValidationWithMessage( + giptm.full_clean, "generic_ip", ["Enter a valid IPv4 or IPv6 address."] + )