diff --git a/django/contrib/localflavor/in_/forms.py b/django/contrib/localflavor/in_/forms.py index afc5534f92c..50db790143a 100644 --- a/django/contrib/localflavor/in_/forms.py +++ b/django/contrib/localflavor/in_/forms.py @@ -5,10 +5,31 @@ import re from django.core.validators import EMPTY_VALUES from django.forms import ValidationError -from django.forms.fields import Field, RegexField, Select +from django.forms.fields import Field, RegexField, CharField, Select from django.utils.encoding import smart_unicode from django.utils.translation import ugettext_lazy as _ +phone_digits_re = re.compile(r""" +( + (?P # the std-code group + ^0 # all std-codes start with 0 + ( + (?P\d{2}) | # either two, three or four digits + (?P\d{3}) | # following the 0 + (?P\d{4}) + ) + ) + [-\s] # space or - + (?P # the phone number group + [1-6] # first digit of phone number + ( + (?(twodigit)\d{7}) | # 7 more phone digits for 3 digit stdcode + (?(threedigit)\d{6}) | # 6 more phone digits for 4 digit stdcode + (?(fourdigit)\d{5}) # 5 more phone digits for 5 digit stdcode + ) + ) +)$""", re.VERBOSE) + class INZipCodeField(RegexField): default_error_messages = { @@ -27,6 +48,7 @@ class INZipCodeField(RegexField): value = re.sub(r'^(\d{3})\s(\d{3})$', r'\1\2', value) return value + class INStateField(Field): """ A form field that validates its input is a Indian state name or @@ -53,6 +75,7 @@ class INStateField(Field): pass raise ValidationError(self.error_messages['invalid']) + class INStateSelect(Select): """ A Select widget that uses a list of Indian states/territories as its @@ -62,3 +85,28 @@ class INStateSelect(Select): from in_states import STATE_CHOICES super(INStateSelect, self).__init__(attrs, choices=STATE_CHOICES) + +class INPhoneNumberField(CharField): + """ + INPhoneNumberField validates that the data is a valid Indian phone number, + including the STD code. It's normalised to 0XXX-XXXXXXX or 0XXX XXXXXXX + format. The first string is the STD code which is a '0' followed by 2-4 + digits. The second string is 8 digits if the STD code is 3 digits, 7 + digits if the STD code is 4 digits and 6 digits if the STD code is 5 + digits. The second string will start with numbers between 1 and 6. The + separator is either a space or a hyphen. + """ + default_error_messages = { + 'invalid': _('Phone numbers must be in 02X-8X or 03X-7X or 04X-6X format.'), + } + + def clean(self, value): + super(INPhoneNumberField, self).clean(value) + if value in EMPTY_VALUES: + return u'' + value = smart_unicode(value) + m = phone_digits_re.match(value) + if m: + return u'%s' % (value) + raise ValidationError(self.error_messages['invalid']) + diff --git a/docs/ref/contrib/localflavor.txt b/docs/ref/contrib/localflavor.txt index 3ad5899239b..60539e96630 100644 --- a/docs/ref/contrib/localflavor.txt +++ b/docs/ref/contrib/localflavor.txt @@ -576,22 +576,34 @@ Iceland (``is_``) India (``in_``) =============== -.. class:: in.forms.INStateField +.. class:: in_.forms.INStateField A form field that validates input as an Indian state/territory name or abbreviation. Input is normalized to the standard two-letter vehicle registration abbreviation for the given state or territory. -.. class:: in.forms.INZipCodeField +.. class:: in_.forms.INZipCodeField A form field that validates input as an Indian zip code, with the format XXXXXXX. -.. class:: in.forms.INStateSelect +.. class:: in_.forms.INStateSelect A ``Select`` widget that uses a list of Indian states/territories as its choices. +.. versionadded:: 1.4 + +.. class:: in_.forms.INPhoneNumberField + + A form field that validates that the data is a valid Indian phone number, + including the STD code. It's normalised to 0XXX-XXXXXXX or 0XXX XXXXXXX + format. The first string is the STD code which is a '0' followed by 2-4 + digits. The second string is 8 digits if the STD code is 3 digits, 7 + digits if the STD code is 4 digits and 6 digits if the STD code is 5 + digits. The second string will start with numbers between 1 and 6. The + separator is either a space or a hyphen. + Ireland (``ie``) ================ diff --git a/tests/regressiontests/forms/localflavor/in_.py b/tests/regressiontests/forms/localflavor/in_.py index 17553667a79..5a04fd1cf4b 100644 --- a/tests/regressiontests/forms/localflavor/in_.py +++ b/tests/regressiontests/forms/localflavor/in_.py @@ -1,12 +1,32 @@ import warnings from django.contrib.localflavor.in_.forms import (INZipCodeField, - INStateField, INStateSelect) + INStateField, INStateSelect, INPhoneNumberField) from utils import LocalFlavorTestCase + class INLocalFlavorTests(LocalFlavorTestCase): + def test_INPhoneNumberField(self): + error_format = [u'Phone numbers must be in 02X-8X or 03X-7X or 04X-6X format.'] + valid = { + '0423-2443667': '0423-2443667', + '0423 2443667': '0423 2443667', + '04236-244366': '04236-244366', + '040-24436678': '040-24436678', + } + invalid = { + '04-2443667': error_format, + '423-2443667': error_format, + '0423-9442667': error_format, + '0423-0443667': error_format, + '0423-244366': error_format, + '04232442667': error_format, + '0423DJANGO': error_format, + } + self.assertFieldOutput(INPhoneNumberField, valid, invalid) + def test_INPStateSelect(self): f = INStateSelect() out = u'''