Ticket #811: genericipaddressfield.diff

File genericipaddressfield.diff, 29.4 KB (added by Sasha Romijn, 13 years ago)

GenericIPAddressField patch

  • docs/topics/forms/modelforms.txt

     
    8383
    8484    ``IPAddressField``               ``IPAddressField``
    8585
     86    ``GenericIPAddressField``            ``GenericIPAddressField``
     87
    8688    ``ManyToManyField``              ``ModelMultipleChoiceField`` (see
    8789                                     below)
    8890
  • docs/ref/models/fields.txt

     
    760760An IP address, in string format (e.g. "192.0.2.30"). The admin represents this
    761761as an ``<input type="text">`` (a single-line input).
    762762
     763``GenericIPAddressField``
     764-------------------------
     765
     766.. class:: GenericIPAddressField([protocols=both, unpack_ipv4_mapped=False, **options])
     767
     768.. versionadded:: 1.4
     769
     770An IPv4 or IPv6 address, in string format (e.g. "192.0.2.30" or "2a02:42fe::4")
     771The admin represents this as an ``<input type="text">`` (a single-line input).
     772
     773The IPv6 address normalization follows `RFC4291 section 2.2`_, including using
     774the IPv4 format suggested in paragraph 3 of that section, like ``::ffff:192.0.2.0``.
     775For example, ``2001:0::0:01`` would be normalized to ``2001::1``, and ``::ffff:0a0a:0a0a``
     776to ``::ffff:10.10.10.10``. All characters are converted to lowercase.
     777
     778.. _RFC4291 section 2.2: http://tools.ietf.org/html/rfc4291#section-2.2
     779
     780.. attribute:: GenericIPAddressField.protocol
     781
     782    Limits valid inputs to the specified protocol.
     783    Accepted values are ``both`` (default), ``IPv4``
     784    or ``IPv6``. Matching is case insensitive.
     785
     786.. attribute:: GenericIPAddressField.unpack_ipv4_mapped
     787
     788    Unpack IPv4 mapped addresses, like ``::ffff::192.0.2.1``.
     789    If this option is enabled, that address would be unpacked to
     790    ``192.0.2.1``. Default is disabled. Can only be used
     791    when ``protocol`` is set to ``both``.
     792
    763793``NullBooleanField``
    764794--------------------
    765795
  • docs/ref/validators.txt

     
    130130    A :class:`RegexValidator` instance that ensures a value looks like an IPv4
    131131    address.
    132132
     133``validate_ipv6_address``
     134-------------------------
     135.. versionadded:: 1.4
     136
     137.. data:: validate_ipv6_address
     138
     139    Uses :mod:`django.utils.ipv6` to check the validity of an IPv6 address.
     140
     141``validate_ipv46_address``
     142--------------------------
     143.. versionadded:: 1.4
     144
     145.. data:: validate_ipv46_address
     146
     147    Uses both ``validate_ipv4_address`` and ``validate_ipv6_address`` to ensure
     148    a value is either a valid IPv4 or IPv6 address.
     149
    133150``validate_comma_separated_integer_list``
    134151-----------------------------------------
    135152.. data:: validate_comma_separated_integer_list
  • docs/ref/forms/fields.txt

     
    622622      expression.
    623623    * Error message keys: ``required``, ``invalid``
    624624
     625``GenericIPAddressField``
     626~~~~~~~~~~~~~~~~~~~~~~~~~
     627
     628.. class:: GenericIPAddressField(**kwargs)
     629
     630.. versionadded:: 1.4
     631
     632A 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. 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
     642The IPv6 address normalization follows `RFC4291 section 2.2`_, including using
     643the IPv4 format suggested in paragraph 3 of that section, like ``::ffff:192.0.2.0``.
     644For example, ``2001:0::0:01`` would be normalized to ``2001::1``, and ``::ffff:0a0a:0a0a``
     645to ``::ffff:10.10.10.10``. All characters are converted to lowercase.
     646
     647.. _RFC4291 section 2.2: http://tools.ietf.org/html/rfc4291#section-2.2
     648
     649Takes two optional arguments:
     650
     651.. attribute:: GenericIPAddressField.protocol
     652
     653    Limits valid inputs to the specified protocol.
     654    Accepted values are ``both`` (default), ``IPv4``
     655    or ``IPv6``. Matching is case insensitive.
     656
     657.. attribute:: GenericIPAddressField.unpack_ipv4_mapped
     658
     659    Unpack IPv4 mapped addresses, like ``::ffff::192.0.2.1``.
     660    If this option is enabled, that address would be unpacked to
     661    ``192.0.2.1``. Default is disabled. Can only be used
     662    when ``protocol`` is set to ``both``.
     663
    625664``MultipleChoiceField``
    626665~~~~~~~~~~~~~~~~~~~~~~~
    627666
  • django/db/models/fields/__init__.py

     
    1717from django.utils.translation import ugettext_lazy as _
    1818from django.utils.encoding import smart_unicode, force_unicode, smart_str
    1919from django.utils import datetime_safe
     20from django.utils.ipv6 import (clean_ipv6_address, is_valid_ipv6_address,
     21    validate_configure_ipaddressfield_settings)
    2022
    2123class NOT_PROVIDED:
    2224    pass
     
    920922
    921923class IPAddressField(Field):
    922924    empty_strings_allowed = False
    923     description = _("IP address")
     925    description = _("IPv4 address")
    924926    def __init__(self, *args, **kwargs):
    925927        kwargs['max_length'] = 15
    926928        Field.__init__(self, *args, **kwargs)
     
    933935        defaults.update(kwargs)
    934936        return super(IPAddressField, self).formfield(**defaults)
    935937
     938class GenericIPAddressField(Field):
     939    empty_strings_allowed = True
     940    description = _("IP address")
     941    default_error_messages = {}
     942   
     943    def __init__(self, protocol='both', unpack_ipv4_mapped=False, *args, **kwargs):
     944        self.unpack_ipv4_mapped = unpack_ipv4_mapped
     945
     946        self.default_validators = validate_configure_ipaddressfield_settings(
     947                protocol, unpack_ipv4_mapped, self.default_error_messages)
     948
     949        kwargs['max_length'] = 39
     950        Field.__init__(self, *args, **kwargs)
     951
     952    def get_internal_type(self):
     953        return "GenericIPAddressField"
     954
     955    def to_python(self, value):
     956        if value and ':' in value:
     957            return clean_ipv6_address(value, self.unpack_ipv4_mapped, self.default_error_messages['invalid'])
     958               
     959        return value
     960
     961    def get_prep_value(self, value):       
     962        if value and ':' in value:
     963            try:
     964                return clean_ipv6_address(value, self.unpack_ipv4_mapped)
     965            except ValidationError:
     966                pass
     967               
     968        return value
     969
     970    def formfield(self, **kwargs):
     971        defaults = {'form_class': forms.GenericIPAddressField}
     972        defaults.update(kwargs)
     973        return super(GenericIPAddressField, self).formfield(**defaults)
     974       
     975
    936976class NullBooleanField(Field):
    937977    empty_strings_allowed = False
    938978    default_error_messages = {
  • django/db/backends/sqlite3/creation.py

     
    2020        'IntegerField':                 'integer',
    2121        'BigIntegerField':              'bigint',
    2222        'IPAddressField':               'char(15)',
     23        'GenericIPAddressField':        '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        'GenericIPAddressField': 'char(39)',
    2223        'NullBooleanField':  'bool',
    2324        'OneToOneField':     'integer',
    2425        'PositiveIntegerField': 'integer UNSIGNED',
  • django/db/backends/oracle/creation.py

     
    2727        'IntegerField':                 'NUMBER(11)',
    2828        'BigIntegerField':              'NUMBER(19)',
    2929        'IPAddressField':               'VARCHAR2(15)',
     30        'GenericIPAddressField':        'VARCHAR2(39)',
    3031        'NullBooleanField':             'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
    3132        'OneToOneField':                'NUMBER(11)',
    3233        'PositiveIntegerField':         'NUMBER(11) CHECK (%(qn_column)s >= 0)',
  • django/db/backends/postgresql_psycopg2/introspection.py

     
    1212        700: 'FloatField',
    1313        701: 'FloatField',
    1414        869: 'IPAddressField',
     15        869: 'GenericIPAddressField',
    1516        1043: 'CharField',
    1617        1082: 'DateField',
    1718        1083: 'TimeField',
  • django/db/backends/postgresql_psycopg2/creation.py

     
    2121        'IntegerField':      'integer',
    2222        'BigIntegerField':   'bigint',
    2323        'IPAddressField':    'inet',
     24        'GenericIPAddressField': 'inet',
    2425        'NullBooleanField':  'boolean',
    2526        'OneToOneField':     'integer',
    2627        'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
  • django/forms/fields.py

     
    99import time
    1010import urlparse
    1111import warnings
     12import socket
    1213from decimal import Decimal, DecimalException
    1314try:
    1415    from cStringIO import StringIO
     
    2122from django.utils.translation import ugettext_lazy as _
    2223from django.utils.encoding import smart_unicode, smart_str, force_unicode
    2324from django.utils.functional import lazy
     25from django.utils.ipv6 import clean_ipv6_address, validate_configure_ipaddressfield_settings
    2426
    2527# Provide this import for backwards compatibility.
    2628from django.core.validators import EMPTY_VALUES
     
    3739    'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
    3840    'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
    3941    'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
    40     'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField',
    41     'TypedChoiceField', 'TypedMultipleChoiceField'
     42    'SplitDateTimeField', 'IPAddressField', 'GenericIPAddressField', 'FilePathField',
     43    'SlugField', 'TypedChoiceField', 'TypedMultipleChoiceField'
    4244)
    4345
    4446
     
    955957    default_validators = [validators.validate_ipv4_address]
    956958
    957959
     960class GenericIPAddressField(CharField):
     961    default_error_messages = {}
     962   
     963    def __init__(self, protocol='both', unpack_ipv4_mapped=False, *args, **kwargs):
     964        self.unpack_ipv4_mapped = unpack_ipv4_mapped
     965
     966        self.default_validators = validate_configure_ipaddressfield_settings(
     967                protocol, unpack_ipv4_mapped, self.default_error_messages)
     968       
     969        super(GenericIPAddressField, self).__init__(*args, **kwargs)
     970       
     971    def to_python(self, value):
     972        if not value:
     973            return ''
     974       
     975        if value and ':' in value:
     976                return clean_ipv6_address(value, self.unpack_ipv4_mapped, self.default_error_messages['invalid'])
     977
     978        return value
     979               
     980
    958981class SlugField(CharField):
    959982    default_error_messages = {
    960983        'invalid': _(u"Enter a valid 'slug' consisting of letters, numbers,"
  • django/core/validators.py

     
    55from django.core.exceptions import ValidationError
    66from django.utils.translation import ugettext_lazy as _
    77from django.utils.encoding import smart_unicode
     8from django.utils.ipv6 import is_valid_ipv6_address
    89
    910# These values, if given to validate(), will trigger the self.required check.
    1011EMPTY_VALUES = (None, '', [], (), {})
     
    145146ipv4_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}$')
    146147validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid')
    147148
     149def validate_ipv6_address(value):
     150    if not is_valid_ipv6_address(value):
     151        raise ValidationError(_(u'Enter a valid IPv6 address.'), code='invalid')
     152
     153def validate_ipv46_address(value):
     154    try:
     155        validate_ipv4_address(value)
     156    except ValidationError:
     157        try:
     158            validate_ipv6_address(value)
     159        except ValidationError:
     160            raise ValidationError(_(u'Enter a valid IPv4 or IPv6 address.'), code='invalid')
     161           
    148162comma_separated_int_list_re = re.compile('^[\d,]+$')
    149163validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _(u'Enter only digits separated by commas.'), 'invalid')
    150164
  • tests/modeltests/validators/tests.py

     
    5252    (validate_ipv4_address, '25,1,1,1', ValidationError),
    5353    (validate_ipv4_address, '25.1 .1.1', ValidationError),
    5454
     55    # validate_ipv6_address uses django.utils.ipv6, which
     56    # is tested in much greater detail in it's own testcase
     57    (validate_ipv6_address, 'fe80::1', None),
     58    (validate_ipv6_address, '::1', None),
     59    (validate_ipv6_address, '1:2:3:4:5:6:7:8', None),
     60
     61    (validate_ipv6_address, '1:2', ValidationError),
     62    (validate_ipv6_address, '::zzz', ValidationError),
     63    (validate_ipv6_address, '12345::', ValidationError),
     64
     65    (validate_ipv46_address, '1.1.1.1', None),
     66    (validate_ipv46_address, '255.0.0.0', None),
     67    (validate_ipv46_address, '0.0.0.0', None),
     68    (validate_ipv46_address, 'fe80::1', None),
     69    (validate_ipv46_address, '::1', None),
     70    (validate_ipv46_address, '1:2:3:4:5:6:7:8', None),
     71
     72    (validate_ipv46_address, '256.1.1.1', ValidationError),
     73    (validate_ipv46_address, '25.1.1.', ValidationError),
     74    (validate_ipv46_address, '25,1,1,1', ValidationError),
     75    (validate_ipv46_address, '25.1 .1.1', ValidationError),
     76    (validate_ipv46_address, '1:2', ValidationError),
     77    (validate_ipv46_address, '::zzz', ValidationError),
     78    (validate_ipv46_address, '12345::', ValidationError),
     79
    5580    (validate_comma_separated_integer_list, '1', None),
    5681    (validate_comma_separated_integer_list, '1,2,3', None),
    5782    (validate_comma_separated_integer_list, '1,2,3,', None),
  • tests/modeltests/validation/tests.py

     
    22from django.test import TestCase
    33from django.core.exceptions import NON_FIELD_ERRORS
    44from modeltests.validation import ValidationTestCase
    5 from modeltests.validation.models import Author, Article, ModelToValidate
     5from modeltests.validation.models import (Author, Article, ModelToValidate,
     6    GenericIPAddressTestModel, GenericIPAddressWithUnpackUniqueTestModel)
    67
    78# Import other tests for this package.
    89from modeltests.validation.validators import TestModelsWithValidators
     
    7778        mtv = ModelToValidate(number=10, name='Some Name'*100)
    7879        self.assertFailsValidation(mtv.full_clean, ['name',])
    7980
     81
    8082class ArticleForm(forms.ModelForm):
    8183    class Meta:
    8284        model = Article
     
    124126        article = Article(author_id=self.author.id)
    125127        form = ArticleForm(data, instance=article)
    126128        self.assertEqual(form.errors.keys(), ['pub_date'])
     129
     130
     131class GenericIPAddressFieldTests(ValidationTestCase):
     132   
     133    def test_correct_generic_ip_passes(self):
     134        giptm = GenericIPAddressTestModel(generic_ip="1.2.3.4")
     135        self.assertEqual(None, giptm.full_clean())
     136        giptm = GenericIPAddressTestModel(generic_ip="2001::2")
     137        self.assertEqual(None, giptm.full_clean())
     138   
     139    def test_invalid_generic_ip_raises_error(self):
     140        giptm = GenericIPAddressTestModel(generic_ip="294.4.2.1")
     141        self.assertFailsValidation(giptm.full_clean, ['generic_ip',])
     142        giptm = GenericIPAddressTestModel(generic_ip="1:2")
     143        self.assertFailsValidation(giptm.full_clean, ['generic_ip',])
     144   
     145    def test_correct_v4_ip_passes(self):
     146        giptm = GenericIPAddressTestModel(v4_ip="1.2.3.4")
     147        self.assertEqual(None, giptm.full_clean())
     148   
     149    def test_invalid_v4_ip_raises_error(self):
     150        giptm = GenericIPAddressTestModel(v4_ip="294.4.2.1")
     151        self.assertFailsValidation(giptm.full_clean, ['v4_ip',])
     152        giptm = GenericIPAddressTestModel(v4_ip="2001::2")
     153        self.assertFailsValidation(giptm.full_clean, ['v4_ip',])
     154   
     155    def test_correct_v6_ip_passes(self):
     156        giptm = GenericIPAddressTestModel(v6_ip="2001::2")
     157        self.assertEqual(None, giptm.full_clean())
     158   
     159    def test_invalid_v6_ip_raises_error(self):
     160        giptm = GenericIPAddressTestModel(v6_ip="1.2.3.4")
     161        self.assertFailsValidation(giptm.full_clean, ['v6_ip',])
     162        giptm = GenericIPAddressTestModel(v6_ip="1:2")
     163        self.assertFailsValidation(giptm.full_clean, ['v6_ip',])
     164   
     165    def test_v6_uniqueness_detection(self):
     166        # These two addresses are the same with different syntax
     167        giptm = GenericIPAddressTestModel(generic_ip="2001::1:0:0:0:0:2")
     168        giptm.save()
     169        giptm = GenericIPAddressTestModel(generic_ip="2001:0:1:2")
     170        self.assertFailsValidation(giptm.full_clean, ['generic_ip',])
     171   
     172    def test_v4_unpack_uniqueness_detection(self):
     173        # These two are different, because we are not doing IPv4 unpacking
     174        giptm = GenericIPAddressTestModel(generic_ip="::ffff:10.10.10.10")
     175        giptm.save()
     176        giptm = GenericIPAddressTestModel(generic_ip="10.10.10.10")
     177        self.assertEqual(None, giptm.full_clean())
     178   
     179        # These two are the same, because we are doing IPv4 unpacking
     180        giptm = GenericIPAddressWithUnpackUniqueTestModel(generic_v4unpack_ip="::ffff:18.52.18.52")
     181        giptm.save()
     182        giptm = GenericIPAddressWithUnpackUniqueTestModel(generic_v4unpack_ip="18.52.18.52")
     183        self.assertFailsValidation(giptm.full_clean, ['generic_v4unpack_ip',])
     184       
     185 No newline at end of file
  • tests/modeltests/validation/models.py

     
    8181
    8282class UniqueErrorsModel(models.Model):
    8383    name = models.CharField(max_length=100, unique=True, error_messages={'unique': u'Custom unique name message.'})
    84     number = models.IntegerField(unique=True, error_messages={'unique': u'Custom unique number message.'})
    85  No newline at end of file
     84    number = models.IntegerField(unique=True, error_messages={'unique': u'Custom unique number message.'})
     85   
     86class GenericIPAddressTestModel(models.Model):
     87    generic_ip = models.GenericIPAddressField(blank=True, unique=True)
     88    v4_ip = models.GenericIPAddressField(blank=True, protocol="ipv4")
     89    v6_ip = models.GenericIPAddressField(blank=True, protocol="ipv6")
     90
     91class GenericIPAddressWithUnpackUniqueTestModel(models.Model):
     92    generic_v4unpack_ip = models.GenericIPAddressField(blank=True, unique=True, unpack_ipv4_mapped=True)
     93   
     94 No newline at end of file
  • tests/regressiontests/forms/tests/error_messages.py

     
    196196        self.assertFormErrors([u'REQUIRED'], f.clean, '')
    197197        self.assertFormErrors([u'INVALID IP ADDRESS'], f.clean, '127.0.0')
    198198
     199    def test_generic_ipaddressfield(self):
     200        e = {
     201            'required': 'REQUIRED',
     202            'invalid': 'INVALID IP ADDRESS',
     203        }
     204        f = GenericIPAddressField(error_messages=e)
     205        self.assertFormErrors([u'REQUIRED'], f.clean, '')
     206        self.assertFormErrors([u'INVALID IP ADDRESS'], f.clean, '127.0.0')
     207
    199208    def test_subclassing_errorlist(self):
    200209        class TestForm(Form):
    201210            first_name = CharField()
  • tests/regressiontests/forms/tests/extra.py

     
    460460        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5')
    461461        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5')
    462462
     463
     464    def test_generic_ipaddress_invalid_arguments(self):
     465        self.assertRaises(ValueError, GenericIPAddressField, protocol="hamster")
     466        self.assertRaises(ValueError, GenericIPAddressField, protocol="ipv4", unpack_ipv4_mapped=True)
     467
     468    def test_generic_ipaddress_as_generic(self):
     469        # The edges of the IPv6 validation code are not deeply tested here,
     470        # they are covered in the tests for django.utils.ipv6
     471       
     472        f = GenericIPAddressField()
     473        self.assertFormErrors([u'This field is required.'], f.clean, '')
     474        self.assertFormErrors([u'This field is required.'], f.clean, None)
     475        self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
     476        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo')
     477        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '127.0.0.')
     478        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1.2.3.4.5')
     479        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '256.125.1.5')
     480        self.assertEqual(f.clean('fe80::223:6cff:fe8a:2e8a'), u'fe80::223:6cff:fe8a:2e8a')
     481        self.assertEqual(f.clean('2a02::223:6cff:fe8a:2e8a'), u'2a02::223:6cff:fe8a:2e8a')
     482        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '12345:2:3:4')
     483        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1::2:3::4')
     484        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a')
     485        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8')
     486        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1:2')
     487
     488    def test_generic_ipaddress_as_ipv4_only(self):
     489        f = GenericIPAddressField(protocol="IPv4")
     490        self.assertFormErrors([u'This field is required.'], f.clean, '')
     491        self.assertFormErrors([u'This field is required.'], f.clean, None)
     492        self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
     493        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo')
     494        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '127.0.0.')
     495        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5')
     496        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5')
     497        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'fe80::223:6cff:fe8a:2e8a')
     498        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '2a02::223:6cff:fe8a:2e8a')
     499
     500    def test_generic_ipaddress_as_ipv4_only(self):               
     501        f = GenericIPAddressField(protocol="IPv6")
     502        self.assertFormErrors([u'This field is required.'], f.clean, '')
     503        self.assertFormErrors([u'This field is required.'], f.clean, None)
     504        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '127.0.0.1')
     505        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, 'foo')
     506        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '127.0.0.')
     507        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1.2.3.4.5')
     508        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '256.125.1.5')
     509        self.assertEqual(f.clean('fe80::223:6cff:fe8a:2e8a'), u'fe80::223:6cff:fe8a:2e8a')
     510        self.assertEqual(f.clean('2a02::223:6cff:fe8a:2e8a'), u'2a02::223:6cff:fe8a:2e8a')
     511        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '12345:2:3:4')
     512        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1::2:3::4')
     513        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a')
     514        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8')
     515        self.assertFormErrors([u'Enter a valid IPv6 address.'], f.clean, '1:2')
     516
     517    def test_generic_ipaddress_as_generic_not_required(self):
     518        f = GenericIPAddressField(required=False)
     519        self.assertEqual(f.clean(''), u'')
     520        self.assertEqual(f.clean(None), u'')
     521        self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
     522        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo')
     523        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '127.0.0.')
     524        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1.2.3.4.5')
     525        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '256.125.1.5')
     526        self.assertEqual(f.clean('fe80::223:6cff:fe8a:2e8a'), u'fe80::223:6cff:fe8a:2e8a')
     527        self.assertEqual(f.clean('2a02::223:6cff:fe8a:2e8a'), u'2a02::223:6cff:fe8a:2e8a')
     528        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '12345:2:3:4')
     529        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1::2:3::4')
     530        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, 'foo::223:6cff:fe8a:2e8a')
     531        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1::2:3:4:5:6:7:8')
     532        self.assertFormErrors([u'Enter a valid IPv4 or IPv6 address.'], f.clean, '1:2')
     533
     534    def test_generic_ipaddress_normalisation(self):
     535        # Test the normalising code
     536        f = GenericIPAddressField()
     537        self.assertEqual(f.clean('::ffff:0a0a:0a0a'), u'::ffff:10.10.10.10')
     538        self.assertEqual(f.clean('::ffff:10.10.10.10'), u'::ffff: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        f = GenericIPAddressField(unpack_ipv4_mapped=True)
     543        self.assertEqual(f.clean('::ffff:0a0a:0a0a'), u'10.10.10.10')
     544
     545
    463546    def test_smart_unicode(self):
    464547        class Test:
    465548            def __str__(self):
  • tests/regressiontests/serializers_regress/tests.py

     
    196196    #(XX, ImageData
    197197    (data_obj, 90, IPAddressData, "127.0.0.1"),
    198198    (data_obj, 91, IPAddressData, None),
     199    (data_obj, 95, GenericIPAddressData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"),
     200    (data_obj, 96, GenericIPAddressData, None),
    199201    (data_obj, 100, NullBooleanData, True),
    200202    (data_obj, 101, NullBooleanData, False),
    201203    (data_obj, 102, NullBooleanData, None),
     
    298300    (pk_obj, 682, IntegerPKData, 0),
    299301#     (XX, ImagePKData
    300302    (pk_obj, 690, IPAddressPKData, "127.0.0.1"),
     303    (pk_obj, 695, GenericIPAddressPKData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"),
    301304    # (pk_obj, 700, NullBooleanPKData, True),
    302305    # (pk_obj, 701, NullBooleanPKData, False),
    303306    (pk_obj, 710, PhonePKData, "212-634-5789"),
  • tests/regressiontests/serializers_regress/models.py

     
    5252class IPAddressData(models.Model):
    5353    data = models.IPAddressField(null=True)
    5454
     55class GenericIPAddressData(models.Model):
     56    data = models.GenericIPAddressField(null=True)
     57
    5558class NullBooleanData(models.Model):
    5659    data = models.NullBooleanField(null=True)
    5760
     
    187190class IPAddressPKData(models.Model):
    188191    data = models.IPAddressField(primary_key=True)
    189192
     193class GenericIPAddressPKData(models.Model):
     194    data = models.GenericIPAddressField(primary_key=True)
     195
    190196# This is just a Boolean field with null=True, and we can't test a PK value of NULL.
    191197# class NullBooleanPKData(models.Model):
    192198#     data = models.NullBooleanField(primary_key=True)
  • tests/regressiontests/utils/tests.py

     
    1919from datetime_safe import *
    2020from baseconv import *
    2121from jslex import *
     22from ipv6 import *
Back to Top