Skip to content
10 changes: 8 additions & 2 deletions rest_framework/utils/field_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,17 @@ def get_field_kwargs(field_name, model_field):
if validator is not validators.validate_slug
]

# IPAddressField do not need to include the 'validate_ipv46_address' argument,
# IPAddressField does not need to include the IP address validators,
# as it adds its own based on the 'protocol' argument.
if isinstance(model_field, models.GenericIPAddressField):
kwargs['protocol'] = model_field.protocol
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,
Comment thread
browniebroke marked this conversation as resolved.
)
]
# Our decimal validation is handled in the field code, not validator code.
if isinstance(model_field, models.DecimalField):
Expand Down
90 changes: 86 additions & 4 deletions tests/test_model_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from django.core.exceptions import ImproperlyConfigured
from django.core.serializers.json import DjangoJSONEncoder
from django.core.validators import (
MaxValueValidator, MinLengthValidator, MinValueValidator
MaxLengthValidator, MaxValueValidator, MinLengthValidator,
MinValueValidator
)
from django.db import models
from django.db.models.signals import m2m_changed
Expand Down Expand Up @@ -435,9 +436,90 @@ class Meta:

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))
self.assertEqual(s.errors['address'],
['Enter a valid IPv4 or IPv6 address.'])

def test_ip_address_validation_with_custom_validator(self):
class IPAddressFieldModel(models.Model):
address = models.GenericIPAddressField(
# MaxLengthValidator is an unhashable type
validators=[MaxLengthValidator(15)],
)

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(
s.errors['address'],
[
'Ensure this value has at most 15 characters (it has 17).',
'Enter a valid IPv4 or IPv6 address.',
],
)

def test_ip_address_validation_with_protocol_ipv4(self):
class IPv4AddressFieldModel(models.Model):
address = models.GenericIPAddressField(protocol='IPv4')

class TestSerializer(serializers.ModelSerializer):
class Meta:
model = IPv4AddressFieldModel
fields = '__all__'

expected = dedent("""
TestSerializer():
id = IntegerField(label='ID', read_only=True)
address = IPAddressField(protocol='IPv4')
""")
self.assertEqual(repr(TestSerializer()), expected)

s = TestSerializer(data={'address': 'not an ip address'})
self.assertFalse(s.is_valid())
self.assertEqual(s.errors['address'],
['Enter a valid IPv4 address.'])

# An IPv6 address is not valid for an IPv4-only field.
s = TestSerializer(data={'address': '2001:db8::1'})
self.assertFalse(s.is_valid())
self.assertEqual(s.errors['address'],
['Enter a valid IPv4 address.'])

s = TestSerializer(data={'address': '192.0.2.1'})
self.assertTrue(s.is_valid(), s.errors)

def test_ip_address_validation_with_protocol_ipv6(self):
class IPv6AddressFieldModel(models.Model):
address = models.GenericIPAddressField(protocol='IPv6')

class TestSerializer(serializers.ModelSerializer):
class Meta:
model = IPv6AddressFieldModel
fields = '__all__'

expected = dedent("""
TestSerializer():
id = IntegerField(label='ID', read_only=True)
address = IPAddressField(protocol='IPv6')
""")
self.assertEqual(repr(TestSerializer()), expected)

s = TestSerializer(data={'address': 'not an ip address'})
self.assertFalse(s.is_valid())
self.assertEqual(s.errors['address'],
['Enter a valid IPv6 address.'])

# An IPv4 address is not valid for an IPv6-only field.
s = TestSerializer(data={'address': '192.0.2.1'})
self.assertFalse(s.is_valid())
self.assertEqual(s.errors['address'],
['Enter a valid IPv6 address.'])

s = TestSerializer(data={'address': '2001:db8::1'})
self.assertTrue(s.is_valid(), s.errors)


@pytest.mark.skipif('not postgres_fields')
Expand Down