From c012b8964e596423b68f4ec4b1b57253cf0ae7b0 Mon Sep 17 00:00:00 2001
From: Malcolm Tredinnick <malcolm.tredinnick@gmail.com>
Date: Sun, 16 Sep 2007 11:38:32 +0000
Subject: [PATCH] Fixed #4067 -- Fixed validation of IPAddressFields in
 newforms. Thanks to neils and the team in the Copenhagen sprint group.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6357 bcc190cf-cafb-0310-a4f2-bffc1f526a37
---
 django/db/models/fields/__init__.py  |  5 +++
 django/newforms/fields.py            | 10 ++++-
 docs/newforms.txt                    | 11 +++++-
 tests/regressiontests/forms/tests.py | 55 ++++++++++++++++++++++++++++
 4 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 7a4c15c3c1..6df1e34288 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -880,6 +880,11 @@ class IPAddressField(Field):
     def validate(self, field_data, all_data):
         validators.isValidIPAddress4(field_data, None)
 
+    def formfield(self, **kwargs):
+        defaults = {'form_class': forms.IPAddressField}
+        defaults.update(kwargs)
+        return super(IPAddressField, self).formfield(**defaults)
+
 class NullBooleanField(Field):
     empty_strings_allowed = False
     def __init__(self, *args, **kwargs):
diff --git a/django/newforms/fields.py b/django/newforms/fields.py
index 8fb1d4f392..a39987e1b5 100644
--- a/django/newforms/fields.py
+++ b/django/newforms/fields.py
@@ -26,7 +26,7 @@ __all__ = (
     'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 'BooleanField',
     'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
     'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
-    'SplitDateTimeField',
+    'SplitDateTimeField', 'IPAddressField',
 )
 
 # These values, if given to to_python(), will trigger the self.required check.
@@ -635,3 +635,11 @@ class SplitDateTimeField(MultiValueField):
                 raise ValidationError(ugettext(u'Enter a valid time.'))
             return datetime.datetime.combine(*data_list)
         return None
+
+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}$')
+
+class IPAddressField(RegexField):
+    def __init__(self, *args, **kwargs):
+        RegexField.__init__(self, ipv4_re,
+                            error_message=ugettext(u'Enter a valid IPv4 address.'),
+                            *args, **kwargs)
diff --git a/docs/newforms.txt b/docs/newforms.txt
index 691aee7e44..2c8f67ce32 100644
--- a/docs/newforms.txt
+++ b/docs/newforms.txt
@@ -1284,6 +1284,15 @@ When you use a ``FileField`` on a form, you must also remember to
 Takes two optional arguments for validation, ``max_value`` and ``min_value``.
 These control the range of values permitted in the field.
 
+``IPAddressField``
+~~~~~~~~~~~~~~~~~~
+
+    * Default widget: ``TextInput``
+    * Empty value: ``''`` (an empty string)
+    * Normalizes to: A Unicode object.
+    * Validates that the given value is a valid IPv4 address, using a regular
+      expression.
+
 ``MultipleChoiceField``
 ~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -1710,7 +1719,7 @@ the full list of conversions:
     ``ForeignKey``                   ``ModelChoiceField`` (see below)
     ``ImageField``                   ``ImageField``
     ``IntegerField``                 ``IntegerField``
-    ``IPAddressField``               ``CharField``
+    ``IPAddressField``               ``IPAddressField``
     ``ManyToManyField``              ``ModelMultipleChoiceField`` (see
                                      below)
     ``NullBooleanField``             ``CharField``
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index a3e61c8f06..616d6e5ff5 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -3834,6 +3834,61 @@ ValidationError: [u'This field is required.']
 >>> f.cleaned_data
 {'field1': u'some text,JP,2007-04-25 06:24:00'}
 
+
+# IPAddressField ##################################################################
+
+>>> f = IPAddressField()
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean('127.0.0.1')
+u'127.0.0.1'
+>>> f.clean('foo')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid IPv4 address.']
+>>> f.clean('127.0.0.')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid IPv4 address.']
+>>> f.clean('1.2.3.4.5')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid IPv4 address.']
+>>> f.clean('256.125.1.5')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid IPv4 address.']
+
+>>> f = IPAddressField(required=False)
+>>> f.clean('')
+u''
+>>> f.clean(None)
+u''
+>>> f.clean('127.0.0.1')
+u'127.0.0.1'
+>>> f.clean('foo')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid IPv4 address.']
+>>> f.clean('127.0.0.')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid IPv4 address.']
+>>> f.clean('1.2.3.4.5')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid IPv4 address.']
+>>> f.clean('256.125.1.5')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid IPv4 address.']
+
 #################################
 # Tests of underlying functions #
 #################################