mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-01 00:17:40 +03:00 
			
		
		
		
	Merge pull request #3008 from linovia/feature/ipaddress-fix
`IPAddressField` improvements
This commit is contained in:
		
						commit
						a3d6601e09
					
				|  | @ -192,6 +192,17 @@ A field that ensures the input is a valid UUID string. The `to_internal_value` m | ||||||
|     - `'urn'` - RFC 4122 URN representation of the UUID: `"urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"` |     - `'urn'` - RFC 4122 URN representation of the UUID: `"urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"` | ||||||
|   Changing the `format` parameters only affects representation values. All formats are accepted by `to_internal_value` |   Changing the `format` parameters only affects representation values. All formats are accepted by `to_internal_value` | ||||||
| 
 | 
 | ||||||
|  | ## IPAddressField | ||||||
|  | 
 | ||||||
|  | A field that ensures the input is a valid IPv4 or IPv6 string. | ||||||
|  | 
 | ||||||
|  | Corresponds to `django.forms.fields.IPAddressField` and `django.forms.fields.GenericIPAddressField`. | ||||||
|  | 
 | ||||||
|  | **Signature**: `IPAddressField(protocol='both', unpack_ipv4=False, **options)` | ||||||
|  | 
 | ||||||
|  | - `protocol` Limits valid inputs to the specified protocol. Accepted values are 'both' (default), 'IPv4' or 'IPv6'. Matching is case insensitive. | ||||||
|  | - `unpack_ipv4` Unpacks IPv4 mapped addresses like ::ffff:192.0.2.1. If this option is enabled that address would be unpacked to 192.0.2.1. Default is disabled. Can only be used when protocol is set to 'both'. | ||||||
|  | 
 | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| # Numeric fields | # Numeric fields | ||||||
|  |  | ||||||
|  | @ -2,12 +2,13 @@ from __future__ import unicode_literals | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.core.exceptions import ObjectDoesNotExist | from django.core.exceptions import ObjectDoesNotExist | ||||||
| from django.core.exceptions import ValidationError as DjangoValidationError | from django.core.exceptions import ValidationError as DjangoValidationError | ||||||
| from django.core.validators import RegexValidator | from django.core.validators import RegexValidator, ip_address_validators | ||||||
| from django.forms import ImageField as DjangoImageField | from django.forms import ImageField as DjangoImageField | ||||||
| from django.utils import six, timezone | from django.utils import six, timezone | ||||||
| from django.utils.dateparse import parse_date, parse_datetime, parse_time | from django.utils.dateparse import parse_date, parse_datetime, parse_time | ||||||
| from django.utils.encoding import is_protected_type, smart_text | from django.utils.encoding import is_protected_type, smart_text | ||||||
| from django.utils.translation import ugettext_lazy as _ | from django.utils.translation import ugettext_lazy as _ | ||||||
|  | from django.utils.ipv6 import clean_ipv6_address | ||||||
| from rest_framework import ISO_8601 | from rest_framework import ISO_8601 | ||||||
| from rest_framework.compat import ( | from rest_framework.compat import ( | ||||||
|     EmailValidator, MinValueValidator, MaxValueValidator, |     EmailValidator, MinValueValidator, MaxValueValidator, | ||||||
|  | @ -672,6 +673,31 @@ class UUIDField(Field): | ||||||
|             return getattr(value, self.uuid_format) |             return getattr(value, self.uuid_format) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class IPAddressField(CharField): | ||||||
|  |     """Support both IPAddressField and GenericIPAddressField""" | ||||||
|  | 
 | ||||||
|  |     default_error_messages = { | ||||||
|  |         'invalid': _('Enter a valid IPv4 or IPv6 address.'), | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def __init__(self, protocol='both', **kwargs): | ||||||
|  |         self.protocol = protocol.lower() | ||||||
|  |         self.unpack_ipv4 = (self.protocol == 'both') | ||||||
|  |         super(IPAddressField, self).__init__(**kwargs) | ||||||
|  |         validators, error_message = ip_address_validators(protocol, self.unpack_ipv4) | ||||||
|  |         self.validators.extend(validators) | ||||||
|  | 
 | ||||||
|  |     def to_internal_value(self, data): | ||||||
|  |         if data and ':' in data: | ||||||
|  |             try: | ||||||
|  |                 if self.protocol in ('both', 'ipv6'): | ||||||
|  |                     return clean_ipv6_address(data, self.unpack_ipv4) | ||||||
|  |             except DjangoValidationError: | ||||||
|  |                 self.fail('invalid', value=data) | ||||||
|  | 
 | ||||||
|  |         return super(IPAddressField, self).to_internal_value(data) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # Number types... | # Number types... | ||||||
| 
 | 
 | ||||||
| class IntegerField(Field): | class IntegerField(Field): | ||||||
|  |  | ||||||
|  | @ -734,6 +734,7 @@ class ModelSerializer(Serializer): | ||||||
|         models.TextField: CharField, |         models.TextField: CharField, | ||||||
|         models.TimeField: TimeField, |         models.TimeField: TimeField, | ||||||
|         models.URLField: URLField, |         models.URLField: URLField, | ||||||
|  |         models.GenericIPAddressField: IPAddressField, | ||||||
|     } |     } | ||||||
|     if ModelDurationField is not None: |     if ModelDurationField is not None: | ||||||
|         serializer_field_mapping[ModelDurationField] = DurationField |         serializer_field_mapping[ModelDurationField] = DurationField | ||||||
|  | @ -1360,6 +1361,10 @@ class ModelSerializer(Serializer): | ||||||
| if hasattr(models, 'UUIDField'): | if hasattr(models, 'UUIDField'): | ||||||
|     ModelSerializer.serializer_field_mapping[models.UUIDField] = UUIDField |     ModelSerializer.serializer_field_mapping[models.UUIDField] = UUIDField | ||||||
| 
 | 
 | ||||||
|  | # IPAddressField is deprecated in Django | ||||||
|  | if hasattr(models, 'IPAddressField'): | ||||||
|  |     ModelSerializer.serializer_field_mapping[models.IPAddressField] = IPAddressField | ||||||
|  | 
 | ||||||
| if postgres_fields: | if postgres_fields: | ||||||
|     class CharMappingField(DictField): |     class CharMappingField(DictField): | ||||||
|         child = CharField() |         child = CharField() | ||||||
|  |  | ||||||
|  | @ -549,6 +549,60 @@ class TestUUIDField(FieldValues): | ||||||
|         self._test_format('hex', '0' * 32) |         self._test_format('hex', '0' * 32) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class TestIPAddressField(FieldValues): | ||||||
|  |     """ | ||||||
|  |     Valid and invalid values for `IPAddressField` | ||||||
|  |     """ | ||||||
|  |     valid_inputs = { | ||||||
|  |         '127.0.0.1': '127.0.0.1', | ||||||
|  |         '192.168.33.255': '192.168.33.255', | ||||||
|  |         '2001:0db8:85a3:0042:1000:8a2e:0370:7334': '2001:db8:85a3:42:1000:8a2e:370:7334', | ||||||
|  |         '2001:cdba:0:0:0:0:3257:9652': '2001:cdba::3257:9652', | ||||||
|  |         '2001:cdba::3257:9652': '2001:cdba::3257:9652' | ||||||
|  |     } | ||||||
|  |     invalid_inputs = { | ||||||
|  |         '127001': ['Enter a valid IPv4 or IPv6 address.'], | ||||||
|  |         '127.122.111.2231': ['Enter a valid IPv4 or IPv6 address.'], | ||||||
|  |         '2001:::9652': ['Enter a valid IPv4 or IPv6 address.'], | ||||||
|  |         '2001:0db8:85a3:0042:1000:8a2e:0370:73341': ['Enter a valid IPv4 or IPv6 address.'], | ||||||
|  |     } | ||||||
|  |     outputs = {} | ||||||
|  |     field = serializers.IPAddressField() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TestIPv4AddressField(FieldValues): | ||||||
|  |     """ | ||||||
|  |     Valid and invalid values for `IPAddressField` | ||||||
|  |     """ | ||||||
|  |     valid_inputs = { | ||||||
|  |         '127.0.0.1': '127.0.0.1', | ||||||
|  |         '192.168.33.255': '192.168.33.255', | ||||||
|  |     } | ||||||
|  |     invalid_inputs = { | ||||||
|  |         '127001': ['Enter a valid IPv4 address.'], | ||||||
|  |         '127.122.111.2231': ['Enter a valid IPv4 address.'], | ||||||
|  |     } | ||||||
|  |     outputs = {} | ||||||
|  |     field = serializers.IPAddressField(protocol='IPv4') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TestIPv6AddressField(FieldValues): | ||||||
|  |     """ | ||||||
|  |     Valid and invalid values for `IPAddressField` | ||||||
|  |     """ | ||||||
|  |     valid_inputs = { | ||||||
|  |         '2001:0db8:85a3:0042:1000:8a2e:0370:7334': '2001:db8:85a3:42:1000:8a2e:370:7334', | ||||||
|  |         '2001:cdba:0:0:0:0:3257:9652': '2001:cdba::3257:9652', | ||||||
|  |         '2001:cdba::3257:9652': '2001:cdba::3257:9652' | ||||||
|  |     } | ||||||
|  |     invalid_inputs = { | ||||||
|  |         '2001:::9652': ['Enter a valid IPv4 or IPv6 address.'], | ||||||
|  |         '2001:0db8:85a3:0042:1000:8a2e:0370:73341': ['Enter a valid IPv4 or IPv6 address.'], | ||||||
|  |     } | ||||||
|  |     outputs = {} | ||||||
|  |     field = serializers.IPAddressField(protocol='IPv6') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # Number types... | # Number types... | ||||||
| 
 | 
 | ||||||
| class TestIntegerField(FieldValues): | class TestIntegerField(FieldValues): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user