Index: django/db/models/fields/__init__.py
===================================================================
--- django/db/models/fields/__init__.py	(revision 14734)
+++ django/db/models/fields/__init__.py	(working copy)
@@ -913,15 +913,22 @@
 class IPAddressField(Field):
     empty_strings_allowed = False
     description = _("IP address")
-    def __init__(self, *args, **kwargs):
-        kwargs['max_length'] = 15
+    def __init__(self, allowv4=True, allowv6=False, *args, **kwargs):
+        assert allowv4 or allowv6, "%ss must have allowv4=True and/or allowv6=True." % self.__class__.__name__
+        self.allowv4, self.allowv6 = allowv4, allowv6
+        if allowv6:
+            kwargs['max_length'] = 39
+        else:
+            kwargs['max_length'] = 15
         Field.__init__(self, *args, **kwargs)
 
     def get_internal_type(self):
         return "IPAddressField"
 
     def formfield(self, **kwargs):
-        defaults = {'form_class': forms.IPAddressField}
+        defaults = {'form_class': forms.IPAddressField,
+                    'allowv4': self.allowv4,
+                    'allowv6': self.allowv6}
         defaults.update(kwargs)
         return super(IPAddressField, self).formfield(**defaults)
 
Index: django/db/backends/sqlite3/creation.py
===================================================================
--- django/db/backends/sqlite3/creation.py	(revision 14734)
+++ django/db/backends/sqlite3/creation.py	(working copy)
@@ -19,7 +19,7 @@
         'FloatField':                   'real',
         'IntegerField':                 'integer',
         'BigIntegerField':              'bigint',
-        'IPAddressField':               'char(15)',
+        'IPAddressField':               'char(%(max_length)s)',
         'NullBooleanField':             'bool',
         'OneToOneField':                'integer',
         'PositiveIntegerField':         'integer unsigned',
Index: django/db/backends/mysql/creation.py
===================================================================
--- django/db/backends/mysql/creation.py	(revision 14734)
+++ django/db/backends/mysql/creation.py	(working copy)
@@ -18,7 +18,7 @@
         'FloatField':        'double precision',
         'IntegerField':      'integer',
         'BigIntegerField':   'bigint',
-        'IPAddressField':    'char(15)',
+        'IPAddressField':    'char(%(max_length)s)',
         'NullBooleanField':  'bool',
         'OneToOneField':     'integer',
         'PositiveIntegerField': 'integer UNSIGNED',
Index: django/db/backends/oracle/creation.py
===================================================================
--- django/db/backends/oracle/creation.py	(revision 14734)
+++ django/db/backends/oracle/creation.py	(working copy)
@@ -26,7 +26,7 @@
         'FloatField':                   'DOUBLE PRECISION',
         'IntegerField':                 'NUMBER(11)',
         'BigIntegerField':              'NUMBER(19)',
-        'IPAddressField':               'VARCHAR2(15)',
+        'IPAddressField':               'VARCHAR2(%(max_length)s)',
         'NullBooleanField':             'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
         'OneToOneField':                'NUMBER(11)',
         'PositiveIntegerField':         'NUMBER(11) CHECK (%(qn_column)s >= 0)',
Index: django/forms/fields.py
===================================================================
--- django/forms/fields.py	(revision 14734)
+++ django/forms/fields.py	(working copy)
@@ -919,10 +919,18 @@
 
 
 class IPAddressField(CharField):
+    def __init__(self, allowv4=True, allowv6=False, *args, **kwargs):
+        self.allowv4, self.allowv6 = allowv4, allowv6
+        super(IPAddressField, self).__init__(*args, **kwargs)
+        if allowv4 and allowv6:
+            self.validators.append(validators.validate_ip_address)
+        elif allowv4:
+            self.validators.append(validators.validate_ipv4_address)
+        elif allowv6:
+            self.validators.append(validators.validate_ipv6_address)
     default_error_messages = {
-        'invalid': _(u'Enter a valid IPv4 address.'),
+        'invalid': _(u'Enter a valid IP address.'),
     }
-    default_validators = [validators.validate_ipv4_address]
 
 
 class SlugField(CharField):
Index: django/core/validators.py
===================================================================
--- django/core/validators.py	(revision 14734)
+++ django/core/validators.py	(working copy)
@@ -4,6 +4,7 @@
 from django.core.exceptions import ValidationError
 from django.utils.translation import ugettext_lazy as _
 from django.utils.encoding import smart_unicode
+from django.utils.ip import ipv6_normalize
 
 # These values, if given to validate(), will trigger the self.required check.
 EMPTY_VALUES = (None, '', [], (), {})
@@ -19,14 +20,17 @@
     regex = ''
     message = _(u'Enter a valid value.')
     code = 'invalid'
+    normalizer = None
 
-    def __init__(self, regex=None, message=None, code=None):
+    def __init__(self, regex=None, message=None, code=None, normalizer=None):
         if regex is not None:
             self.regex = regex
         if message is not None:
             self.message = message
         if code is not None:
             self.code = code
+        if normalizer is not None:
+            self.normalizer = normalizer
 
         if isinstance(self.regex, basestring):
             self.regex = re.compile(regex)
@@ -35,6 +39,12 @@
         """
         Validates that the input matches the regular expression.
         """
+        if self.normalizer:
+            try:
+                value = self.normalizer(value)
+            except ValueError:
+                raise ValidationError(self.message, code=self.code)
+        
         if not self.regex.search(smart_unicode(value)):
             raise ValidationError(self.message, code=self.code)
 
@@ -125,6 +135,18 @@
 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}$')
 validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid')
 
+ipv6_re = re.compile(r'^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$') 
+validate_ipv6_address = RegexValidator(ipv6_re, _(u'Enter a valid IPv6 address.'), 'invalid', ipv6_normalize)
+
+def validate_ip_address(value):
+    try:
+        validate_ipv4_address(value)
+    except ValidationError:
+        try:
+            validate_ipv6_address(value)
+        except ValidationError:
+            raise ValidationError(_(u'Enter a valid IP address.'), code='invalid')
+
 comma_separated_int_list_re = re.compile('^[\d,]+$')
 validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _(u'Enter only digits separated by commas.'), 'invalid')
 
Index: django/utils/ip.py
===================================================================
--- django/utils/ip.py	(revision 0)
+++ django/utils/ip.py	(revision 0)
@@ -0,0 +1,54 @@
+def ipv6_normalize(addr):
+    """
+    Normalize an IPv6 address to allow easy regexp validation.
+    Mostly checks the length, and gets rid of tricky things
+    like IPv4 mapped addresses and :: shortcuts
+    
+    Outputs a string
+    """
+    # Some basic error checking
+    if addr.count('::') > 2 or ':::' in addr:
+        raise ValueError
+
+    ip = addr.split(':')
+    nbfull = len([elem for elem in ip if elem != ''])
+    nb = len(ip)
+    
+    if nb < 3:
+        # The minimal IPv6 address is :: so at least 3 parts after split
+        raise ValueError
+
+    if nbfull >= 1 and '.' in ip[-1]:
+        # Convert IPv4 mapped addresses to full hexadecimal notation
+        ipv4 = ip[-1].split('.')
+        hex1 = (int(ipv4[0]) << 8) + int(ipv4[1])
+        hex2 = (int(ipv4[2]) << 8) + int(ipv4[3])
+        ip[-1:] = [hex(hex1)[2:], hex(hex2)[2:]]
+        nbfull = nbfull + 1
+        nb = nb + 1
+
+    if nbfull == 8 or nbfull == nb:
+        # No need to bother
+        return addr
+    elif nbfull > 8:
+        # Has to be invalid anyway
+        raise ValueError
+
+    # Begin normalization
+    start, end, index = (None, None, 0)
+    for elem in ip:
+        if elem == '':
+            if start is None:
+                start = index
+                end = index
+            else:
+                end = index
+        index += 1
+    pad = 8 - nbfull
+    if end != start:
+        ip[start:end-start+1] = ['0'] * pad
+    else:
+        ip[start] = '0'
+        if pad > 1:
+            ip[start:1] = ['0'] * (pad - 1)
+    return ':'.join([item for item in ip if len(item) > 0])

Property changes on: django/utils/ip.py
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: tests/regressiontests/forms/tests/error_messages.py
===================================================================
--- tests/regressiontests/forms/tests/error_messages.py	(revision 14734)
+++ tests/regressiontests/forms/tests/error_messages.py	(working copy)
@@ -192,7 +192,7 @@
             'required': 'REQUIRED',
             'invalid': 'INVALID IP ADDRESS',
         }
-        f = IPAddressField(error_messages=e)
+        f = IPAddressField(error_messages=e, allowv4=True, allowv6=True)
         self.assertFormErrors([u'REQUIRED'], f.clean, '')
         self.assertFormErrors([u'INVALID IP ADDRESS'], f.clean, '127.0.0')
 
Index: tests/regressiontests/forms/tests/extra.py
===================================================================
--- tests/regressiontests/forms/tests/extra.py	(revision 14734)
+++ tests/regressiontests/forms/tests/extra.py	(working copy)
@@ -437,24 +437,78 @@
         self.assertEqual(f.cleaned_data['field1'], u'some text,JP,2007-04-25 06:24:00')
 
     def test_ipaddress(self):
-        f = IPAddressField()
+        f = IPAddressField(allowv4=True, allowv6=False)
         self.assertFormErrors([u'This field is required.'], f.clean, '')
         self.assertFormErrors([u'This field is required.'], f.clean, None)
         self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
-        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo')
-        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '127.0.0.')
-        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5')
-        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '::1')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
 
-        f = IPAddressField(required=False)
+        f = IPAddressField(allowv4=False, allowv6=True)
+        self.assertFormErrors([u'This field is required.'], f.clean, '')
+        self.assertFormErrors([u'This field is required.'], f.clean, None)
+        self.assertEqual(f.clean('::1'), u'::1')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.1')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
+
+        f = IPAddressField(allowv4=True, allowv6=True)
+        self.assertFormErrors([u'This field is required.'], f.clean, '')
+        self.assertFormErrors([u'This field is required.'], f.clean, None)
+        self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
+        self.assertEqual(f.clean('::1'), u'::1')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
+
+        f = IPAddressField(required=False, allowv4=True, allowv6=False)
         self.assertEqual(f.clean(''), u'')
         self.assertEqual(f.clean(None), u'')
         self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
-        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo')
-        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '127.0.0.')
-        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5')
-        self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '::1')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
 
+        f = IPAddressField(required=False, allowv4=False, allowv6=True)
+        self.assertEqual(f.clean(''), u'')
+        self.assertEqual(f.clean(None), u'')
+        self.assertEqual(f.clean('::1'), u'::1')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.1')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
+
+        f = IPAddressField(required=False, allowv4=True, allowv6=True)
+        self.assertEqual(f.clean(''), u'')
+        self.assertEqual(f.clean(None), u'')
+        self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
+        self.assertEqual(f.clean('::1'), u'::1')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, 'foo')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '127.0.0.')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1.2.3.4.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '256.125.1.5')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7')
+        self.assertFormErrors([u'Enter a valid IP address.'], f.clean, '1:2:3:4:5:6:7:8:9')
+
     def test_smart_unicode(self):
         class Test:
             def __str__(self):
Index: tests/regressiontests/serializers_regress/tests.py
===================================================================
--- tests/regressiontests/serializers_regress/tests.py	(revision 14734)
+++ tests/regressiontests/serializers_regress/tests.py	(working copy)
@@ -195,6 +195,7 @@
     #(XX, ImageData
     (data_obj, 90, IPAddressData, "127.0.0.1"),
     (data_obj, 91, IPAddressData, None),
+    (data_obj, 92, IPAddressData, "::1"),
     (data_obj, 100, NullBooleanData, True),
     (data_obj, 101, NullBooleanData, False),
     (data_obj, 102, NullBooleanData, None),
@@ -300,6 +301,7 @@
     (pk_obj, 682, IntegerPKData, 0),
 #     (XX, ImagePKData
     (pk_obj, 690, IPAddressPKData, "127.0.0.1"),
+    (pk_obj, 691, IPAddressPKData, "::1"),
     # (pk_obj, 700, NullBooleanPKData, True),
     # (pk_obj, 701, NullBooleanPKData, False),
     (pk_obj, 710, PhonePKData, "212-634-5789"),
Index: tests/regressiontests/serializers_regress/models.py
===================================================================
--- tests/regressiontests/serializers_regress/models.py	(revision 14734)
+++ tests/regressiontests/serializers_regress/models.py	(working copy)
@@ -50,7 +50,7 @@
 #    data = models.ImageField(null=True)
 
 class IPAddressData(models.Model):
-    data = models.IPAddressField(null=True)
+    data = models.IPAddressField(null=True, allowv4=True, allowv6=True)
 
 class NullBooleanData(models.Model):
     data = models.NullBooleanField(null=True)
@@ -188,7 +188,7 @@
 #    data = models.ImageField(primary_key=True)
 
 class IPAddressPKData(models.Model):
-    data = models.IPAddressField(primary_key=True)
+    data = models.IPAddressField(primary_key=True, allowv4=True, allowv6=True)
 
 # This is just a Boolean field with null=True, and we can't test a PK value of NULL.
 # class NullBooleanPKData(models.Model):
