Ticket #811: ipv6-9781.diff

File ipv6-9781.diff, 12.4 KB (added by Johann Queuniet, 16 years ago)

IPv6 patch for trunk, revision 9781

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

     
    710710        defaults.update(kwargs)
    711711        return super(IPAddressField, self).formfield(**defaults)
    712712
     713class IP6AddressField(Field):
     714    empty_strings_allowed = False
     715    def __init__(self, *args, **kwargs):
     716        kwargs['max_length'] = 39
     717        Field.__init__(self, *args, **kwargs)
     718
     719    def get_internal_type(self):
     720        return "IP6AddressField"
     721
     722    def formfield(self, **kwargs):
     723        defaults = {'form_class': forms.IP6AddressField}
     724        defaults.update(kwargs)
     725        return super(IP6AddressField, self).formfield(**defaults)
     726
     727
    713728class NullBooleanField(Field):
    714729    empty_strings_allowed = False
    715730    def __init__(self, *args, **kwargs):
  • django/db/backends/postgresql/introspection.py

     
    88        23: 'IntegerField',
    99        25: 'TextField',
    1010        701: 'FloatField',
    11         869: 'IPAddressField',
     11        869: 'IP6AddressField',
    1212        1043: 'CharField',
    1313        1082: 'DateField',
    1414        1083: 'TimeField',
  • django/db/backends/postgresql/creation.py

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

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

     
    1919        'FloatField':        'double precision',
    2020        'IntegerField':      'integer',
    2121        'IPAddressField':    'char(15)',
     22        'IP6AddressField':   'char(39)',
    2223        'NullBooleanField':  'bool',
    2324        'OneToOneField':     'integer',
    2425        'PositiveIntegerField': 'integer UNSIGNED',
     
    6364                field.rel.to._meta.db_table, field.rel.to._meta.pk.column)
    6465            ]
    6566        return table_output, deferred
    66        
    67  Pas de fin de ligne à la fin du fichier
     67       
  • django/db/backends/oracle/creation.py

     
    2828        'FloatField':                   'DOUBLE PRECISION',
    2929        'IntegerField':                 'NUMBER(11)',
    3030        'IPAddressField':               'VARCHAR2(15)',
     31        'IP6AddressField':              '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

     
    2626import django.core.exceptions
    2727from django.utils.translation import ugettext_lazy as _
    2828from django.utils.encoding import smart_unicode, smart_str
     29from django.utils.http import ip6_normalize
    2930
    3031from util import ErrorList, ValidationError
    3132from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget
     
    4041    'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
    4142    'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
    4243    'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField',
    43     'TypedChoiceField'
     44    'TypedChoiceField', 'IP6AddressField'
    4445)
    4546
    4647# These values, if given to to_python(), will trigger the self.required check.
     
    881882    def __init__(self, *args, **kwargs):
    882883        super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs)
    883884
     885ipv6_re = re.compile(r'^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$')
     886
     887class IP6AddressField(CharField):
     888    def __init__(self, *args, **kwargs):
     889        super(IP6AddressField, self).__init__(39, 3, *args, **kwargs)
     890
     891    def clean(self, value):
     892        value = super(IP6AddressField, self).clean(value)
     893        if value == u'':
     894            return value
     895        if not ipv4_re.search(value):
     896            try:
     897                ip = ip6_normalize(value)
     898                if not ipv6_re.search(ip):
     899                    raise ValidationError(_(u'Enter a valid IP address.'))
     900            except ValueError:
     901                raise ValidationError(_(u'Enter a valid IP address.'))
     902        return value
     903
    884904slug_re = re.compile(r'^[-\w]+$')
    885905
    886906class SlugField(RegexField):
  • django/utils/http.py

     
    9494        i = i % j
    9595        factor -= 1
    9696    return ''.join(base36)
     97
     98
     99def ip6_normalize(addr):
     100    """
     101    Normalize an IPv6 address to allow easy regexp validation
     102    mostly checks the length, and gets ride of tricky things
     103    like IPv4 mapped addresses and :: shortcuts
     104   
     105    Outputs a string
     106    """
     107    # Some basic error checking
     108    if addr.count('::') > 2 or ':::' in addr:
     109        raise ValueError
     110
     111    ip = addr.split(':')
     112    nbfull = len([elem for elem in ip if elem != ''])
     113    nb = len(ip)
     114
     115    if nbfull >= 1 and '.' in ip[-1]:
     116        # Convert IPv4 mapped addresses to full hexa
     117        ipv4 = ip[-1].split('.')
     118        hex1 = (int(ipv4[0]) << 8) + int(ipv4[1])
     119        hex2 = (int(ipv4[2]) << 8) + int(ipv4[3])
     120        ip[-1:] = [hex(hex1)[2:], hex(hex2)[2:]]
     121        nbfull = nbfull + 1
     122        nb = nb + 1
     123
     124    if nbfull == 8 or nbfull == nb:
     125        # No need to bother
     126        return addr
     127    elif nbfull > 8:
     128        # Has to be invalid anyway
     129        raise ValueError
     130
     131    # Begin normalization
     132    start, end, index = (None, None, 0)
     133    for elem in ip:
     134        if elem == '':
     135            if start is None:
     136                start = index
     137                end = index
     138            else:
     139                end = index
     140        index += 1
     141    pad = 8 - nbfull
     142    if end != start:
     143        ip[start:end-start+1] = ['0'] * pad
     144    else:
     145        ip[start] = '0'
     146        if pad > 1:
     147            ip[start:1] = ['0'] * (pad - 1)
     148    return ':'.join([item for item in ip if len(item) > 0])
  • tests/regressiontests/forms/extra.py

     
    377377...
    378378ValidationError: [u'Enter a valid IPv4 address.']
    379379
     380# IP6AddressField ##################################################################
     381
     382>>> f = IP6AddressField()
     383>>> f.clean('')
     384Traceback (most recent call last):
     385...
     386ValidationError: [u'This field is required.']
     387>>> f.clean(None)
     388Traceback (most recent call last):
     389...
     390ValidationError: [u'This field is required.']
     391>>> f.clean('127.0.0.1')
     392u'127.0.0.1'
     393>>> f.clean('2a01:05d8:25ae:9451:020d:39bc:21e6:8cab')
     394u'2a01:05d8:25ae:9451:020d:39bc:21e6:8cab'
     395>>> f.clean('2a01:5d8:25ae:9451:20d:39bc:21e6:8cab')
     396u'2a01:5d8:25ae:9451:20d:39bc:21e6:8cab'
     397>>> f.clean('::1')
     398u'::1'
     399>>> f.clean('::ffff:88.191.32.1')
     400u'::ffff:88.191.32.1'
     401>>> f.clean('foo')
     402Traceback (most recent call last):
     403...
     404ValidationError: [u'Enter a valid IP address.']
     405>>> f.clean('127.0.0.')
     406Traceback (most recent call last):
     407...
     408ValidationError: [u'Enter a valid IP address.']
     409>>> f.clean('::1:')
     410Traceback (most recent call last):
     411...
     412ValidationError: [u'Enter a valid IP address.']
     413>>> f.clean('1.2.3.4.5')
     414Traceback (most recent call last):
     415...
     416ValidationError: [u'Enter a valid IP address.']
     417>>> f.clean('2a01:5d8:25ae:9451:20d:39bc:21e6:8cab:fb2c')
     418Traceback (most recent call last):
     419...
     420ValidationError: [u'Ensure this value has at most 39 characters (it has 42).']
     421>>> f.clean('2a01:5d8:25ae:9451:20d:39bc:1e6:cab:b2c')
     422Traceback (most recent call last):
     423...
     424ValidationError: [u'Enter a valid IP address.']
     425>>> f.clean('256.125.1.5')
     426Traceback (most recent call last):
     427...
     428ValidationError: [u'Enter a valid IP address.']
     429>>> f.clean('::12345')
     430Traceback (most recent call last):
     431...
     432ValidationError: [u'Enter a valid IP address.']
     433
     434>>> f = IP6AddressField(required=False)
     435>>> f.clean('')
     436u''
     437>>> f.clean(None)
     438u''
     439>>> f.clean('127.0.0.1')
     440u'127.0.0.1'
     441>>> f.clean('foo')
     442Traceback (most recent call last):
     443...
     444ValidationError: [u'Enter a valid IP address.']
     445>>> f.clean('127.0.0.')
     446Traceback (most recent call last):
     447...
     448ValidationError: [u'Enter a valid IP address.']
     449>>> f.clean('1.2.3.4.5')
     450Traceback (most recent call last):
     451...
     452ValidationError: [u'Enter a valid IP address.']
     453>>> f.clean('256.125.1.5')
     454Traceback (most recent call last):
     455...
     456ValidationError: [u'Enter a valid IP address.']
     457
     458
    380459#################################
    381460# Tests of underlying functions #
    382461#################################
  • docs/topics/forms/modelforms.txt

     
    6060    ``ImageField``                   ``ImageField``
    6161    ``IntegerField``                 ``IntegerField``
    6262    ``IPAddressField``               ``IPAddressField``
     63    ``IP6AddressField``              ``IP6AddressField``
    6364    ``ManyToManyField``              ``ModelMultipleChoiceField`` (see
    6465                                     below)
    6566    ``NullBooleanField``             ``CharField``
  • docs/ref/models/fields.txt

     
    626626
    627627.. class:: IPAddressField([**options])
    628628
    629 An IP address, in string format (e.g. "192.0.2.30"). The admin represents this
     629An IPv4 address, in string format (e.g. "192.0.2.30"). The admin represents this
    630630as an ``<input type="text">`` (a single-line input).
    631631
     632``IP6AddressField``
     633------------------
     634
     635.. class:: IP6AddressField([**options])
     636
     637An IPv4 or IPv6 address, in string format (e.g. "192.0.2.30" or
     638"2a01:5d8:25ae:9451:20d:39bc:1e6:cab:b2c"). The admin represents this as an
     639``<input type="text">`` (a single-line input).
     640
    632641``NullBooleanField``
    633642--------------------
    634643
  • docs/ref/forms/fields.txt

     
    623623      expression.
    624624    * Error message keys: ``required``, ``invalid``
    625625
     626``IP6AddressField``
     627~~~~~~~~~~~~~~~~~~
     628
     629.. class:: IP6AddressField(**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