Ticket #811: ipv6-14734.diff

File ipv6-14734.diff, 17.0 KB (added by Sander Steffann, 13 years ago)

Implementing suggested changes

  • django/db/models/fields/__init__.py

     
    913913class IPAddressField(Field):
    914914    empty_strings_allowed = False
    915915    description = _("IP address")
    916     def __init__(self, *args, **kwargs):
    917         kwargs['max_length'] = 15
     916    def __init__(self, allowv4=True, allowv6=False, *args, **kwargs):
     917        assert allowv4 or allowv6, "%ss must have allowv4=True and/or allowv6=True." % self.__class__.__name__
     918        self.allowv4, self.allowv6 = allowv4, allowv6
     919        if allowv6:
     920            kwargs['max_length'] = 39
     921        else:
     922            kwargs['max_length'] = 15
    918923        Field.__init__(self, *args, **kwargs)
    919924
    920925    def get_internal_type(self):
    921926        return "IPAddressField"
    922927
    923928    def formfield(self, **kwargs):
    924         defaults = {'form_class': forms.IPAddressField}
     929        defaults = {'form_class': forms.IPAddressField,
     930                    'allowv4': self.allowv4,
     931                    'allowv6': self.allowv6}
    925932        defaults.update(kwargs)
    926933        return super(IPAddressField, self).formfield(**defaults)
    927934
  • django/db/backends/sqlite3/creation.py

     
    1919        'FloatField':                   'real',
    2020        'IntegerField':                 'integer',
    2121        'BigIntegerField':              'bigint',
    22         'IPAddressField':               'char(15)',
     22        'IPAddressField':               'char(%(max_length)s)',
    2323        'NullBooleanField':             'bool',
    2424        'OneToOneField':                'integer',
    2525        'PositiveIntegerField':         'integer unsigned',
  • django/db/backends/mysql/creation.py

     
    1818        'FloatField':        'double precision',
    1919        'IntegerField':      'integer',
    2020        'BigIntegerField':   'bigint',
    21         'IPAddressField':    'char(15)',
     21        'IPAddressField':    'char(%(max_length)s)',
    2222        'NullBooleanField':  'bool',
    2323        'OneToOneField':     'integer',
    2424        'PositiveIntegerField': 'integer UNSIGNED',
  • django/db/backends/oracle/creation.py

     
    2626        'FloatField':                   'DOUBLE PRECISION',
    2727        'IntegerField':                 'NUMBER(11)',
    2828        'BigIntegerField':              'NUMBER(19)',
    29         'IPAddressField':               'VARCHAR2(15)',
     29        'IPAddressField':               'VARCHAR2(%(max_length)s)',
    3030        'NullBooleanField':             'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
    3131        'OneToOneField':                'NUMBER(11)',
    3232        'PositiveIntegerField':         'NUMBER(11) CHECK (%(qn_column)s >= 0)',
  • django/forms/fields.py

     
    919919
    920920
    921921class IPAddressField(CharField):
     922    def __init__(self, allowv4=True, allowv6=False, *args, **kwargs):
     923        self.allowv4, self.allowv6 = allowv4, allowv6
     924        super(IPAddressField, self).__init__(*args, **kwargs)
     925        if allowv4 and allowv6:
     926            self.validators.append(validators.validate_ip_address)
     927        elif allowv4:
     928            self.validators.append(validators.validate_ipv4_address)
     929        elif allowv6:
     930            self.validators.append(validators.validate_ipv6_address)
    922931    default_error_messages = {
    923         'invalid': _(u'Enter a valid IPv4 address.'),
     932        'invalid': _(u'Enter a valid IP address.'),
    924933    }
    925     default_validators = [validators.validate_ipv4_address]
    926934
    927935
    928936class SlugField(CharField):
  • django/core/validators.py

     
    44from django.core.exceptions import ValidationError
    55from django.utils.translation import ugettext_lazy as _
    66from django.utils.encoding import smart_unicode
     7from django.utils.ip import ipv6_normalize
    78
    89# These values, if given to validate(), will trigger the self.required check.
    910EMPTY_VALUES = (None, '', [], (), {})
     
    1920    regex = ''
    2021    message = _(u'Enter a valid value.')
    2122    code = 'invalid'
     23    normalizer = None
    2224
    23     def __init__(self, regex=None, message=None, code=None):
     25    def __init__(self, regex=None, message=None, code=None, normalizer=None):
    2426        if regex is not None:
    2527            self.regex = regex
    2628        if message is not None:
    2729            self.message = message
    2830        if code is not None:
    2931            self.code = code
     32        if normalizer is not None:
     33            self.normalizer = normalizer
    3034
    3135        if isinstance(self.regex, basestring):
    3236            self.regex = re.compile(regex)
     
    3539        """
    3640        Validates that the input matches the regular expression.
    3741        """
     42        if self.normalizer:
     43            try:
     44                value = self.normalizer(value)
     45            except ValueError:
     46                raise ValidationError(self.message, code=self.code)
     47       
    3848        if not self.regex.search(smart_unicode(value)):
    3949            raise ValidationError(self.message, code=self.code)
    4050
     
    125135ipv4_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}$')
    126136validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid')
    127137
     138ipv6_re = re.compile(r'^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$')
     139validate_ipv6_address = RegexValidator(ipv6_re, _(u'Enter a valid IPv6 address.'), 'invalid', ipv6_normalize)
     140
     141def validate_ip_address(value):
     142    try:
     143        validate_ipv4_address(value)
     144    except ValidationError:
     145        try:
     146            validate_ipv6_address(value)
     147        except ValidationError:
     148            raise ValidationError(_(u'Enter a valid IP address.'), code='invalid')
     149
    128150comma_separated_int_list_re = re.compile('^[\d,]+$')
    129151validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _(u'Enter only digits separated by commas.'), 'invalid')
    130152
  • django/utils/ip.py

     
     1def ipv6_normalize(addr):
     2    """
     3    Normalize an IPv6 address to allow easy regexp validation.
     4    Mostly checks the length, and gets rid of tricky things
     5    like IPv4 mapped addresses and :: shortcuts
     6   
     7    Outputs a string
     8    """
     9    # Some basic error checking
     10    if addr.count('::') > 2 or ':::' in addr:
     11        raise ValueError
     12
     13    ip = addr.split(':')
     14    nbfull = len([elem for elem in ip if elem != ''])
     15    nb = len(ip)
     16   
     17    if nb < 3:
     18        # The minimal IPv6 address is :: so at least 3 parts after split
     19        raise ValueError
     20
     21    if nbfull >= 1 and '.' in ip[-1]:
     22        # Convert IPv4 mapped addresses to full hexadecimal notation
     23        ipv4 = ip[-1].split('.')
     24        hex1 = (int(ipv4[0]) << 8) + int(ipv4[1])
     25        hex2 = (int(ipv4[2]) << 8) + int(ipv4[3])
     26        ip[-1:] = [hex(hex1)[2:], hex(hex2)[2:]]
     27        nbfull = nbfull + 1
     28        nb = nb + 1
     29
     30    if nbfull == 8 or nbfull == nb:
     31        # No need to bother
     32        return addr
     33    elif nbfull > 8:
     34        # Has to be invalid anyway
     35        raise ValueError
     36
     37    # Begin normalization
     38    start, end, index = (None, None, 0)
     39    for elem in ip:
     40        if elem == '':
     41            if start is None:
     42                start = index
     43                end = index
     44            else:
     45                end = index
     46        index += 1
     47    pad = 8 - nbfull
     48    if end != start:
     49        ip[start:end-start+1] = ['0'] * pad
     50    else:
     51        ip[start] = '0'
     52        if pad > 1:
     53            ip[start:1] = ['0'] * (pad - 1)
     54    return ':'.join([item for item in ip if len(item) > 0])
  • tests/regressiontests/forms/tests/error_messages.py

    Property changes on: django/utils/ip.py
    ___________________________________________________________________
    Added: svn:mime-type
       + text/plain
    
     
    192192            'required': 'REQUIRED',
    193193            'invalid': 'INVALID IP ADDRESS',
    194194        }
    195         f = IPAddressField(error_messages=e)
     195        f = IPAddressField(error_messages=e, allowv4=True, allowv6=True)
    196196        self.assertFormErrors([u'REQUIRED'], f.clean, '')
    197197        self.assertFormErrors([u'INVALID IP ADDRESS'], f.clean, '127.0.0')
    198198
  • tests/regressiontests/forms/tests/extra.py

     
    437437        self.assertEqual(f.cleaned_data['field1'], u'some text,JP,2007-04-25 06:24:00')
    438438
    439439    def test_ipaddress(self):
    440         f = IPAddressField()
     440        f = IPAddressField(allowv4=True, allowv6=False)
    441441        self.assertFormErrors([u'This field is required.'], f.clean, '')
    442442        self.assertFormErrors([u'This field is required.'], f.clean, None)
    443443        self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
    444         self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo')
    445         self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '127.0.0.')
    446         self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5')
    447         self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5')
     444        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '::1')
     445        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
     446        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
     447        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
     448        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
     449        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
     450        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
    448451
    449         f = IPAddressField(required=False)
     452        f = IPAddressField(allowv4=False, allowv6=True)
     453        self.assertFormErrors([u'This field is required.'], f.clean, '')
     454        self.assertFormErrors([u'This field is required.'], f.clean, None)
     455        self.assertEqual(f.clean('::1'), u'::1')
     456        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.1')
     457        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
     458        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
     459        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
     460        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
     461        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
     462        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
     463
     464        f = IPAddressField(allowv4=True, allowv6=True)
     465        self.assertFormErrors([u'This field is required.'], f.clean, '')
     466        self.assertFormErrors([u'This field is required.'], f.clean, None)
     467        self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
     468        self.assertEqual(f.clean('::1'), u'::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.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
     474        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
     475
     476        f = IPAddressField(required=False, allowv4=True, allowv6=False)
    450477        self.assertEqual(f.clean(''), u'')
    451478        self.assertEqual(f.clean(None), u'')
    452479        self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
    453         self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo')
    454         self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '127.0.0.')
    455         self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5')
    456         self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5')
     480        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '::1')
     481        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
     482        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
     483        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
     484        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
     485        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
     486        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
    457487
     488        f = IPAddressField(required=False, allowv4=False, allowv6=True)
     489        self.assertEqual(f.clean(''), u'')
     490        self.assertEqual(f.clean(None), u'')
     491        self.assertEqual(f.clean('::1'), u'::1')
     492        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.1')
     493        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
     494        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
     495        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
     496        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
     497        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
     498        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
     499
     500        f = IPAddressField(required=False, allowv4=True, allowv6=True)
     501        self.assertEqual(f.clean(''), u'')
     502        self.assertEqual(f.clean(None), u'')
     503        self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
     504        self.assertEqual(f.clean('::1'), u'::1')
     505        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
     506        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
     507        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
     508        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
     509        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
     510        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
     511
    458512    def test_smart_unicode(self):
    459513        class Test:
    460514            def __str__(self):
  • tests/regressiontests/serializers_regress/tests.py

     
    195195    #(XX, ImageData
    196196    (data_obj, 90, IPAddressData, "127.0.0.1"),
    197197    (data_obj, 91, IPAddressData, None),
     198    (data_obj, 92, IPAddressData, "::1"),
    198199    (data_obj, 100, NullBooleanData, True),
    199200    (data_obj, 101, NullBooleanData, False),
    200201    (data_obj, 102, NullBooleanData, None),
     
    300301    (pk_obj, 682, IntegerPKData, 0),
    301302#     (XX, ImagePKData
    302303    (pk_obj, 690, IPAddressPKData, "127.0.0.1"),
     304    (pk_obj, 691, IPAddressPKData, "::1"),
    303305    # (pk_obj, 700, NullBooleanPKData, True),
    304306    # (pk_obj, 701, NullBooleanPKData, False),
    305307    (pk_obj, 710, PhonePKData, "212-634-5789"),
  • tests/regressiontests/serializers_regress/models.py

     
    5050#    data = models.ImageField(null=True)
    5151
    5252class IPAddressData(models.Model):
    53     data = models.IPAddressField(null=True)
     53    data = models.IPAddressField(null=True, allowv4=True, allowv6=True)
    5454
    5555class NullBooleanData(models.Model):
    5656    data = models.NullBooleanField(null=True)
     
    188188#    data = models.ImageField(primary_key=True)
    189189
    190190class IPAddressPKData(models.Model):
    191     data = models.IPAddressField(primary_key=True)
     191    data = models.IPAddressField(primary_key=True, allowv4=True, allowv6=True)
    192192
    193193# This is just a Boolean field with null=True, and we can't test a PK value of NULL.
    194194# class NullBooleanPKData(models.Model):
Back to Top