Ticket #811: ipv6-13294.diff

File ipv6-13294.diff, 12.9 KB (added by Johann Queuniet <johann.queuniet@…>, 5 years ago)

patch bump to r13294, updated to 1.2 validators

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

     
    906906        defaults.update(kwargs)
    907907        return super(IPAddressField, self).formfield(**defaults)
    908908
     909class IPv6AddressField(Field):
     910    empty_strings_allowed = False
     911    description = _("IPv6/IPv4 address")
     912    def __init__(self, *args, **kwargs):
     913        kwargs['max_length'] = 39
     914        Field.__init__(self, *args, **kwargs)
     915
     916    def get_internal_type(self):
     917        return "IPv6AddressField"
     918
     919    def formfield(self, **kwargs):
     920        defaults = {'form_class': forms.IPv6AddressField}
     921        defaults.update(kwargs)
     922        return super(IPv6AddressField, self).formfield(**defaults)
     923
     924
    909925class NullBooleanField(Field):
    910926    empty_strings_allowed = False
    911927    default_error_messages = {
  • django/db/backends/postgresql/introspection.py

     
    1010        25: 'TextField',
    1111        700: 'FloatField',
    1212        701: 'FloatField',
    13         869: 'IPAddressField',
     13        869: 'IPv6AddressField',
    1414        1043: 'CharField',
    1515        1082: 'DateField',
    1616        1083: 'TimeField',
  • django/db/backends/postgresql/creation.py

     
    1919        'IntegerField':      'integer',
    2020        'BigIntegerField':   'bigint',
    2121        'IPAddressField':    'inet',
     22        'IPv6AddressField':  'inet',
    2223        'NullBooleanField':  'boolean',
    2324        'OneToOneField':     'integer',
    2425        'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
  • django/db/backends/sqlite3/creation.py

     
    2020        'IntegerField':                 'integer',
    2121        'BigIntegerField':              'bigint',
    2222        'IPAddressField':               'char(15)',
     23        'IPv6AddressField':             'char(39)',
    2324        'NullBooleanField':             'bool',
    2425        'OneToOneField':                'integer',
    2526        'PositiveIntegerField':         'integer unsigned',
  • django/db/backends/mysql/creation.py

     
    1919        'IntegerField':      'integer',
    2020        'BigIntegerField':   'bigint',
    2121        'IPAddressField':    'char(15)',
     22        'IPv6AddressField':  'char(39)',
    2223        'NullBooleanField':  'bool',
    2324        'OneToOneField':     'integer',
    2425        'PositiveIntegerField': 'integer UNSIGNED',
  • django/db/backends/oracle/creation.py

     
    2828        'IntegerField':                 'NUMBER(11)',
    2929        'BigIntegerField':              'NUMBER(19)',
    3030        'IPAddressField':               'VARCHAR2(15)',
     31        'IPv6AddressField':             'VARCHAR2(39)',
    3132        'NullBooleanField':             'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
    3233        'OneToOneField':                'NUMBER(11)',
    3334        'PositiveIntegerField':         'NUMBER(11) CHECK (%(qn_column)s >= 0)',
  • django/forms/fields.py

     
    3939    'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
    4040    'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
    4141    'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField',
    42     'TypedChoiceField'
     42    'TypedChoiceField', 'IPv6AddressField'
    4343)
    4444
    4545def en_format(name):
     
    868868    default_validators = [validators.validate_ipv4_address]
    869869
    870870
     871ipv6_re = re.compile(r'^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$')
     872
     873class IPv6AddressField(CharField):
     874    default_error_messages = {
     875        'invalid': _(u'Enter a valid IP address.'),
     876    }
     877    default_validators = [validators.validate_ipv6_address]
     878
     879
    871880class SlugField(CharField):
    872881    default_error_messages = {
    873882        'invalid': _(u"Enter a valid 'slug' consisting of letters, numbers,"
  • django/core/validators.py

     
    3838        if not self.regex.search(smart_unicode(value)):
    3939            raise ValidationError(self.message, code=self.code)
    4040
     41ipv6_re = re.compile(r'^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$')
     42
     43class IPv6Validator(object):
     44    message = _(u'Enter a valid IP address.')
     45    code = 'invalid'
     46
     47    def __init__(self, message=None, code=None):
     48        if message is not None:
     49            self.message = message
     50        if code is not None:
     51            self.code = code
     52
     53    def __call__(self, value):
     54        """
     55        Validates that the input matches an IPv4/IPv6 address
     56        """
     57        ip = smart_unicode(value)
     58        if not ipv4_re.search(ip):
     59            from django.utils.http import ipv6_normalize
     60            try:
     61                ipv6 = ipv6_normalize(ip)
     62                if not ipv6_re.search(ipv6):
     63                    raise ValidationError(self.message, code=self.code)
     64            except ValueError:
     65                    raise ValidationError(self.message, code=self.code)
     66
    4167class URLValidator(RegexValidator):
    4268    regex = re.compile(
    4369        r'^https?://' # http:// or https://
     
    124150
    125151ipv4_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}$')
    126152validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid')
     153validate_ipv6_address = IPv6Validator(_(u'Enter a valid IPv4 address.'), 'invalid')
    127154
    128155comma_separated_int_list_re = re.compile('^[\d,]+$')
    129156validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _(u'Enter only digits separated by commas.'), 'invalid')
  • django/utils/http.py

     
    117117    """
    118118    return '"%s"' % etag.replace('\\', '\\\\').replace('"', '\\"')
    119119
     120def ipv6_normalize(addr):
     121    """
     122    Normalize an IPv6 address to allow easy regexp validation
     123    mostly checks the length, and gets ride of tricky things
     124    like IPv4 mapped addresses and :: shortcuts
     125   
     126    Outputs a string
     127    """
     128    # Some basic error checking
     129    if addr.count('::') > 2 or ':::' in addr:
     130        raise ValueError
     131
     132    ip = addr.split(':')
     133    nbfull = len([elem for elem in ip if elem != ''])
     134    nb = len(ip)
     135
     136    if nbfull >= 1 and '.' in ip[-1]:
     137        # Convert IPv4 mapped addresses to full hexa
     138        ipv4 = ip[-1].split('.')
     139        hex1 = (int(ipv4[0]) << 8) + int(ipv4[1])
     140        hex2 = (int(ipv4[2]) << 8) + int(ipv4[3])
     141        ip[-1:] = [hex(hex1)[2:], hex(hex2)[2:]]
     142        nbfull = nbfull + 1
     143        nb = nb + 1
     144
     145    if nbfull == 8 or nbfull == nb:
     146        # No need to bother
     147        return addr
     148    elif nbfull > 8:
     149        # Has to be invalid anyway
     150        raise ValueError
     151
     152    # Begin normalization
     153    start, end, index = (None, None, 0)
     154    for elem in ip:
     155        if elem == '':
     156            if start is None:
     157                start = index
     158                end = index
     159            else:
     160                end = index
     161        index += 1
     162    pad = 8 - nbfull
     163    if end != start:
     164        ip[start:end-start+1] = ['0'] * pad
     165    else:
     166        ip[start] = '0'
     167        if pad > 1:
     168            ip[start:1] = ['0'] * (pad - 1)
     169    return ':'.join([item for item in ip if len(item) > 0])
  • tests/regressiontests/forms/extra.py

     
    580580...
    581581ValidationError: [u'Enter a valid IPv4 address.']
    582582
     583# IPv6AddressField ##################################################################
     584
     585>>> f = IPv6AddressField()
     586>>> f.clean('')
     587Traceback (most recent call last):
     588...
     589ValidationError: [u'This field is required.']
     590>>> f.clean(None)
     591Traceback (most recent call last):
     592...
     593ValidationError: [u'This field is required.']
     594>>> f.clean('127.0.0.1')
     595u'127.0.0.1'
     596>>> f.clean('2a01:05d8:25ae:9451:020d:39bc:21e6:8cab')
     597u'2a01:05d8:25ae:9451:020d:39bc:21e6:8cab'
     598>>> f.clean('2a01:5d8:25ae:9451:20d:39bc:21e6:8cab')
     599u'2a01:5d8:25ae:9451:20d:39bc:21e6:8cab'
     600>>> f.clean('::1')
     601u'::1'
     602>>> f.clean('::ffff:88.191.32.1')
     603u'::ffff:88.191.32.1'
     604>>> f.clean('foo')
     605Traceback (most recent call last):
     606...
     607ValidationError: [u'Enter a valid IP address.']
     608>>> f.clean('127.0.0.')
     609Traceback (most recent call last):
     610...
     611ValidationError: [u'Enter a valid IP address.']
     612>>> f.clean('::1:')
     613Traceback (most recent call last):
     614...
     615ValidationError: [u'Enter a valid IP address.']
     616>>> f.clean('1.2.3.4.5')
     617Traceback (most recent call last):
     618...
     619ValidationError: [u'Enter a valid IP address.']
     620>>> f.clean('2a01:5d8:25ae:9451:20d:39bc:21e6:8cab:fb2c')
     621Traceback (most recent call last):
     622...
     623ValidationError: [u'Enter a valid IP address.']
     624>>> f.clean('2a01:5d8:25ae:9451:20d:39bc:1e6:cab:b2c')
     625Traceback (most recent call last):
     626...
     627ValidationError: [u'Enter a valid IP address.']
     628>>> f.clean('256.125.1.5')
     629Traceback (most recent call last):
     630...
     631ValidationError: [u'Enter a valid IP address.']
     632>>> f.clean('::12345')
     633Traceback (most recent call last):
     634...
     635ValidationError: [u'Enter a valid IP address.']
     636
     637>>> f = IPv6AddressField(required=False)
     638>>> f.clean('')
     639u''
     640>>> f.clean(None)
     641u''
     642>>> f.clean('127.0.0.1')
     643u'127.0.0.1'
     644>>> f.clean('foo')
     645Traceback (most recent call last):
     646...
     647ValidationError: [u'Enter a valid IP address.']
     648>>> f.clean('127.0.0.')
     649Traceback (most recent call last):
     650...
     651ValidationError: [u'Enter a valid IP address.']
     652>>> f.clean('1.2.3.4.5')
     653Traceback (most recent call last):
     654...
     655ValidationError: [u'Enter a valid IP address.']
     656>>> f.clean('256.125.1.5')
     657Traceback (most recent call last):
     658...
     659ValidationError: [u'Enter a valid IP address.']
     660
     661
    583662#################################
    584663# Tests of underlying functions #
    585664#################################
  • docs/topics/forms/modelforms.txt

     
    7979
    8080    ``IPAddressField``               ``IPAddressField``
    8181
     82    ``IPv6AddressField``              ``IPv6AddressField``
     83
    8284    ``ManyToManyField``              ``ModelMultipleChoiceField`` (see
    8385                                     below)
    8486
  • docs/ref/models/fields.txt

     
    715715
    716716.. class:: IPAddressField([**options])
    717717
    718 An IP address, in string format (e.g. "192.0.2.30"). The admin represents this
     718An IPv4 address, in string format (e.g. "192.0.2.30"). The admin represents this
    719719as an ``<input type="text">`` (a single-line input).
    720720
     721``IPv6AddressField``
     722------------------
     723
     724.. class:: IPv6AddressField([**options])
     725
     726An IPv4 or IPv6 address, in string format (e.g. "192.0.2.30" or
     727"2a01:5d8:25ae:9451:20d:39bc:1e6:cab:b2c"). The admin represents this as an
     728``<input type="text">`` (a single-line input).
     729
    721730``NullBooleanField``
    722731--------------------
    723732
  • docs/ref/forms/fields.txt

     
    623623      expression.
    624624    * Error message keys: ``required``, ``invalid``
    625625
     626``IPv6AddressField``
     627~~~~~~~~~~~~~~~~~~
     628
     629.. class:: IPv6AddressField(**kwargs)
     630
     631    * Default widget: ``TextInput``
     632    * Empty value: ``''`` (an empty string)
     633    * Normalizes to: A Unicode object.
     634    * Validates that the given value is a valid IPv4 or IPv6 address, using
     635      a regular expression.
     636    * Error message keys: ``required``, ``invalid``
     637
     638
    626639``MultipleChoiceField``
    627640~~~~~~~~~~~~~~~~~~~~~~~
    628641
Back to Top