Ticket #811: ipv46address.diff
File ipv46address.diff, 20.9 KB (added by , 13 years ago) |
---|
-
docs/topics/forms/modelforms.txt
83 83 84 84 ``IPAddressField`` ``IPAddressField`` 85 85 86 ``IPv46AddressField`` ``IPv46AddressField`` 87 86 88 ``ManyToManyField`` ``ModelMultipleChoiceField`` (see 87 89 below) 88 90 -
docs/ref/models/fields.txt
755 755 An IP address, in string format (e.g. "192.0.2.30"). The admin represents this 756 756 as an ``<input type="text">`` (a single-line input). 757 757 758 ``IPv46AddressField`` 759 --------------------- 760 761 .. class:: IPv46AddressField([**options]) 762 763 .. versionadded:: 1.4 764 765 An IPv4 or IPv6 address, in string format (e.g. "192.0.2.30" or "2a02:42fe::4") 766 The admin represents this as an ``<input type="text">`` (a single-line input). 767 758 768 ``NullBooleanField`` 759 769 -------------------- 760 770 -
docs/ref/forms/fields.txt
622 622 expression. 623 623 * Error message keys: ``required``, ``invalid`` 624 624 625 ``IPv46AddressField`` 626 ~~~~~~~~~~~~~~~~~~~~~ 627 628 .. class:: IPv46AddressField(**kwargs) 629 630 .. versionadded:: 1.4 631 632 A field containing either an IPv4 or an IPv6 address. 633 634 * Default widget: ``TextInput`` 635 * Empty value: ``''`` (an empty string) 636 * Normalizes to: A Unicode object. By default, IPv6 addresses are 637 normalized as described below. 638 * Validates that the given value is a valid IP address, using regular 639 expressions. 640 * Error message keys: ``required``, ``invalid`` 641 642 The IPv6 address normalisation follows `RFC4291 section 2.2`_, including using 643 the IPv4 format suggested in paragraph 3 of that section. For example, 644 ``2001:0::0:01`` would be compressed to ``2001::1`` and ``::0a0a:0a0a`` 645 to ``::10.10.10.10``. This behaviour can be disabled by setting the 646 ``skip_ipv6_normalisation`` attribute. 647 648 .. _RFC4291 section 2.2: http://tools.ietf.org/html/rfc4291#section-2.2 649 650 Takes three optional arguments: 651 652 .. attribute:: IPv46AddressField.skip_ipv6_normalisation 653 654 Disables the IPv6 address normalisation described above. 655 656 .. attribute:: IPv46AddressField.only_ipv4 657 658 Limits valid inputs to only IPv4 addresses 659 660 .. attribute:: IPv46AddressField.only_ipv6 661 662 Limits valid inputs to only IPv6 addresses 663 664 Setting both ``only_ipv4`` and ``only_ipv6`` is invalid. 665 625 666 ``MultipleChoiceField`` 626 667 ~~~~~~~~~~~~~~~~~~~~~~~ 627 668 -
django/db/models/fields/__init__.py
919 919 920 920 class IPAddressField(Field): 921 921 empty_strings_allowed = False 922 description = _("IP address")922 description = _("IPv4 address") 923 923 def __init__(self, *args, **kwargs): 924 924 kwargs['max_length'] = 15 925 925 Field.__init__(self, *args, **kwargs) … … 932 932 defaults.update(kwargs) 933 933 return super(IPAddressField, self).formfield(**defaults) 934 934 935 class IPv46AddressField(Field): 936 empty_strings_allowed = False 937 description = _("IP address") 938 def __init__(self, *args, **kwargs): 939 kwargs['max_length'] = 39 940 Field.__init__(self, *args, **kwargs) 941 942 def get_internal_type(self): 943 return "IPv46AddressField" 944 945 def formfield(self, **kwargs): 946 defaults = {'form_class': forms.IPv46AddressField} 947 defaults.update(kwargs) 948 return super(IPv46AddressField, self).formfield(**defaults) 949 935 950 class NullBooleanField(Field): 936 951 empty_strings_allowed = False 937 952 default_error_messages = { -
django/db/backends/sqlite3/creation.py
20 20 'IntegerField': 'integer', 21 21 'BigIntegerField': 'bigint', 22 22 'IPAddressField': 'char(15)', 23 'IPv46AddressField': 'char(39)', 23 24 'NullBooleanField': 'bool', 24 25 'OneToOneField': 'integer', 25 26 'PositiveIntegerField': 'integer unsigned', -
django/db/backends/mysql/creation.py
19 19 'IntegerField': 'integer', 20 20 'BigIntegerField': 'bigint', 21 21 'IPAddressField': 'char(15)', 22 'IPv46AddressField': 'char(39)', 22 23 'NullBooleanField': 'bool', 23 24 'OneToOneField': 'integer', 24 25 'PositiveIntegerField': 'integer UNSIGNED', -
django/db/backends/oracle/creation.py
27 27 'IntegerField': 'NUMBER(11)', 28 28 'BigIntegerField': 'NUMBER(19)', 29 29 'IPAddressField': 'VARCHAR2(15)', 30 'IPv46AddressField': 'VARCHAR2(39)', 30 31 'NullBooleanField': 'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))', 31 32 'OneToOneField': 'NUMBER(11)', 32 33 'PositiveIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)', -
django/db/backends/postgresql_psycopg2/introspection.py
12 12 700: 'FloatField', 13 13 701: 'FloatField', 14 14 869: 'IPAddressField', 15 869: 'IPv46AddressField', 15 16 1043: 'CharField', 16 17 1082: 'DateField', 17 18 1083: 'TimeField', -
django/db/backends/postgresql_psycopg2/creation.py
21 21 'IntegerField': 'integer', 22 22 'BigIntegerField': 'bigint', 23 23 'IPAddressField': 'inet', 24 'IPv6AddressField': 'inet', 24 25 'NullBooleanField': 'boolean', 25 26 'OneToOneField': 'integer', 26 27 'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)', -
django/forms/fields.py
9 9 import time 10 10 import urlparse 11 11 import warnings 12 import socket 12 13 from decimal import Decimal, DecimalException 13 14 try: 14 15 from cStringIO import StringIO … … 37 38 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 38 39 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 39 40 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 40 'SplitDateTimeField', 'IPAddressField', ' FilePathField', 'SlugField',41 ' TypedChoiceField', 'TypedMultipleChoiceField'41 'SplitDateTimeField', 'IPAddressField', 'IPv46AddressField', 'FilePathField', 42 'SlugField', 'TypedChoiceField', 'TypedMultipleChoiceField' 42 43 ) 43 44 44 45 … … 947 948 default_validators = [validators.validate_ipv4_address] 948 949 949 950 951 class IPv46AddressField(CharField): 952 def __init__(self, only_ipv4=False, only_ipv6=False, skip_ipv6_normalisation=False, *args, **kwargs): 953 self.skip_ipv6_normalisation = skip_ipv6_normalisation 954 955 if only_ipv4 and only_ipv6: 956 raise ValueError( 957 "You can not simultaniously use both only_ipv4 and only_ipv6 on an IPv46AddressField - " 958 "it would generate a field that could never validate") 959 960 self.default_validators = [validators.validate_ipv46_address] 961 self.default_error_messages['invalid'] = _(u'Enter a valid IP address.') 962 963 if only_ipv4: 964 self.default_validators = [validators.validate_ipv4_address] 965 self.default_error_messages['invalid'] = _(u'Enter a valid IPv4 address.') 966 967 if only_ipv6: 968 self.default_validators = [validators.validate_ipv6_address] 969 self.default_error_messages['invalid'] = _(u'Enter a valid IPv6 address.') 970 971 super(IPv46AddressField, self).__init__(*args, **kwargs) 972 973 def to_python(self, value): 974 "Try to normalize the address using getaddrinfo" 975 if not value: 976 return '' 977 978 if self.skip_ipv6_normalisation: 979 return value 980 981 try: 982 new_value = socket.getaddrinfo(value, None, 0, 0, 0, socket.AI_NUMERICHOST)[0][4][0] 983 return new_value 984 except socket.gaierror: 985 return value 986 987 default_error_messages = {} 988 989 950 990 class SlugField(CharField): 951 991 default_error_messages = { 952 992 'invalid': _(u"Enter a valid 'slug' consisting of letters, numbers," -
django/core/validators.py
143 143 ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') 144 144 validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid') 145 145 146 ipv6_re = re.compile(r'^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$') 147 validate_ipv6_address = RegexValidator(ipv6_re, _(u'Enter a valid IPv6 address.'), 'invalid') 148 149 def validate_ipv46_address(value): 150 try: 151 validate_ipv4_address(value) 152 except ValidationError: 153 try: 154 validate_ipv6_address(value) 155 except ValidationError: 156 raise ValidationError(_(u'Enter a valid IP address.'), code='invalid') 157 146 158 comma_separated_int_list_re = re.compile('^[\d,]+$') 147 159 validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _(u'Enter only digits separated by commas.'), 'invalid') 148 160 -
tests/regressiontests/forms/tests/error_messages.py
196 196 self.assertFormErrors([u'REQUIRED'], f.clean, '') 197 197 self.assertFormErrors([u'INVALID IP ADDRESS'], f.clean, '127.0.0') 198 198 199 def test_ipv46addressfield(self): 200 e = { 201 'required': 'REQUIRED', 202 'invalid': 'INVALID IP ADDRESS', 203 } 204 f = IPv46AddressField(error_messages=e) 205 self.assertFormErrors([u'REQUIRED'], f.clean, '') 206 self.assertFormErrors([u'INVALID IP ADDRESS'], f.clean, '127.0.0') 207 199 208 def test_subclassing_errorlist(self): 200 209 class TestForm(Form): 201 210 first_name = CharField() -
tests/regressiontests/forms/tests/extra.py
460 460 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5') 461 461 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5') 462 462 463 464 def test_ipv46address(self): 465 f = IPv46AddressField() 466 self.assertFormErrors([u'This field is required.'], f.clean, '') 467 self.assertFormErrors([u'This field is required.'], f.clean, None) 468 self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1') 469 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo') 470 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.') 471 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5') 472 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5') 473 self.assertEqual(f.clean('fe80::223:6cff:fe8a:2e8a'), u'fe80::223:6cff:fe8a:2e8a') 474 self.assertEqual(f.clean('2a02::223:6cff:fe8a:2e8a'), u'2a02::223:6cff:fe8a:2e8a') 475 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo') 476 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '12345:2:3:4') 477 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1::2:3::4') 478 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') 479 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1::2:3:4:5:6:7:8') 480 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2') 481 482 f = IPv46AddressField(only_ipv4=True) 483 self.assertFormErrors([u'This field is required.'], f.clean, '') 484 self.assertFormErrors([u'This field is required.'], f.clean, None) 485 self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1') 486 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo') 487 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '127.0.0.') 488 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5') 489 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5') 490 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'fe80::223:6cff:fe8a:2e8a') 491 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '2a02::223:6cff:fe8a:2e8a') 492 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo') 493 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '12345:2:3:4') 494 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1::2:3::4') 495 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') 496 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1::2:3:4:5:6:7:8') 497 self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1:2') 498 499 f = IPv46AddressField(only_ipv6=True) 500 self.assertFormErrors([u'This field is required.'], f.clean, '') 501 self.assertFormErrors([u'This field is required.'], f.clean, None) 502 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '127.0.0.1') 503 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, 'foo') 504 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '127.0.0.') 505 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1.2.3.4.5') 506 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '256.125.1.5') 507 self.assertEqual(f.clean('fe80::223:6cff:fe8a:2e8a'), u'fe80::223:6cff:fe8a:2e8a') 508 self.assertEqual(f.clean('2a02::223:6cff:fe8a:2e8a'), u'2a02::223:6cff:fe8a:2e8a') 509 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, 'foo') 510 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '12345:2:3:4') 511 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1::2:3::4') 512 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') 513 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8') 514 self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1:2') 515 516 f = IPv46AddressField(required=False) 517 self.assertEqual(f.clean(''), u'') 518 self.assertEqual(f.clean(None), u'') 519 self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1') 520 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo') 521 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.') 522 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5') 523 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5') 524 self.assertEqual(f.clean('fe80::223:6cff:fe8a:2e8a'), u'fe80::223:6cff:fe8a:2e8a') 525 self.assertEqual(f.clean('2a02::223:6cff:fe8a:2e8a'), u'2a02::223:6cff:fe8a:2e8a') 526 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo') 527 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '12345:2:3:4') 528 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1::2:3::4') 529 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo::223:6cff:fe8a:2e8a') 530 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1::2:3:4:5:6:7:8') 531 self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2') 532 533 self.assertRaises(ValueError, IPv46AddressField, only_ipv4=True, only_ipv6=True) 534 535 # Test the normalising code 536 f = IPv46AddressField(only_ipv6=True) 537 self.assertEqual(f.clean('::ffff:0a0a:0a0a'), u'::ffff:10.10.10.10') 538 self.assertEqual(f.clean('::0a0a:0a0a'), u'::10.10.10.10') 539 self.assertEqual(f.clean('2001:000:a:0000:0:fe:fe:beef'), u'2001:0:a::fe:fe:beef') 540 self.assertEqual(f.clean('2001::a:0000:0:fe:fe:beef'), u'2001:0:a::fe:fe:beef') 541 542 # Test the normalising code 543 f = IPv46AddressField(only_ipv6=True, skip_ipv6_normalisation=True) 544 self.assertEqual(f.clean('::ffff:0a0a:0a0a'), u'::ffff:0a0a:0a0a') 545 self.assertEqual(f.clean('::0a0a:0a0a'), u'::0a0a:0a0a') 546 self.assertEqual(f.clean('2001:000:a:0000:0:fe:fe:beef'), u'2001:000:a:0000:0:fe:fe:beef') 547 self.assertEqual(f.clean('2001::a:0000:0:fe:fe:beef'), u'2001::a:0000:0:fe:fe:beef') 548 549 463 550 def test_smart_unicode(self): 464 551 class Test: 465 552 def __str__(self): -
tests/regressiontests/serializers_regress/tests.py
196 196 #(XX, ImageData 197 197 (data_obj, 90, IPAddressData, "127.0.0.1"), 198 198 (data_obj, 91, IPAddressData, None), 199 (data_obj, 95, IPv46AddressData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"), 200 (data_obj, 96, IPv46AddressData, None), 199 201 (data_obj, 100, NullBooleanData, True), 200 202 (data_obj, 101, NullBooleanData, False), 201 203 (data_obj, 102, NullBooleanData, None), … … 298 300 (pk_obj, 682, IntegerPKData, 0), 299 301 # (XX, ImagePKData 300 302 (pk_obj, 690, IPAddressPKData, "127.0.0.1"), 303 (pk_obj, 695, IPv46AddressPKData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"), 301 304 # (pk_obj, 700, NullBooleanPKData, True), 302 305 # (pk_obj, 701, NullBooleanPKData, False), 303 306 (pk_obj, 710, PhonePKData, "212-634-5789"), -
tests/regressiontests/serializers_regress/models.py
52 52 class IPAddressData(models.Model): 53 53 data = models.IPAddressField(null=True) 54 54 55 class IPv46AddressData(models.Model): 56 data = models.IPv46AddressField(null=True) 57 55 58 class NullBooleanData(models.Model): 56 59 data = models.NullBooleanField(null=True) 57 60 … … 187 190 class IPAddressPKData(models.Model): 188 191 data = models.IPAddressField(primary_key=True) 189 192 193 class IPv46AddressPKData(models.Model): 194 data = models.IPv46AddressField(primary_key=True) 195 190 196 # This is just a Boolean field with null=True, and we can't test a PK value of NULL. 191 197 # class NullBooleanPKData(models.Model): 192 198 # data = models.NullBooleanField(primary_key=True)