Skip to content

Commit

Permalink
Fixed #5871 -- Factored out the validation errors in localflavor form…
Browse files Browse the repository at this point in the history
… fields. Brings them into line with the standard newforms fields. Patch from Jan Rademaker.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6926 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
malcolmt committed Dec 17, 2007
1 parent 4d7aa81 commit 1fcb4e4
Show file tree
Hide file tree
Showing 23 changed files with 375 additions and 217 deletions.
28 changes: 19 additions & 9 deletions django/contrib/localflavor/ar/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,20 @@ class ARPostalCodeField(RegexField):
See http://www.correoargentino.com.ar/consulta_cpa/home.php
"""
default_error_messages = {
'invalid': ugettext("Enter a postal code in the format NNNN or ANNNNAAA."),
}

def __init__(self, *args, **kwargs):
super(ARPostalCodeField, self).__init__(r'^\d{4}$|^[A-HJ-NP-Za-hj-np-z]\d{4}\D{3}$',
min_length=4, max_length=8,
error_message=ugettext("Enter a postal code in the format NNNN or ANNNNAAA."),
*args, **kwargs)
min_length=4, max_length=8, *args, **kwargs)

def clean(self, value):
value = super(ARPostalCodeField, self).clean(value)
if value in EMPTY_VALUES:
return u''
if len(value) not in (4, 8):
raise ValidationError(ugettext("Enter a postal code in the format NNNN or ANNNNAAA."))
raise ValidationError(self.error_messages['invalid'])
if len(value) == 8:
return u'%s%s%s' % (value[0].upper(), value[1:5], value[5:].upper())
return value
Expand All @@ -44,6 +46,11 @@ class ARDNIField(CharField):
"""
A field that validates `Documento Nacional de Identidad´ (DNI) numbers.
"""
default_error_messages = {
'invalid': ugettext("This field requires only numbers."),
'max_digits': ugettext("This field requires 7 or 8 digits."),
}

def __init__(self, *args, **kwargs):
super(ARDNIField, self).__init__(max_length=10, min_length=7, *args,
**kwargs)
Expand All @@ -58,10 +65,9 @@ def clean(self, value):
if not value.isdigit():
value = value.replace('.', '')
if not value.isdigit():
raise ValidationError(ugettext("This field requires only numbers."))
raise ValidationError(self.error_messages['invalid'])
if len(value) not in (7, 8):
raise ValidationError(
ugettext("This field requires 7 or 8 digits."))
raise ValidationError(self.error_messages['max_digits'])

return value

Expand All @@ -70,9 +76,13 @@ class ARCUITField(RegexField):
This field validates a CUIT (Código Único de Identificación Tributaria). A
CUIT is of the form XX-XXXXXXXX-V. The last digit is a check digit.
"""
default_error_messages = {
'invalid': ugettext('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'),
'checksum': ugettext("Invalid CUIT."),
}

def __init__(self, *args, **kwargs):
super(ARCUITField, self).__init__(r'^\d{2}-?\d{8}-?\d$',
error_message=ugettext('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'),
*args, **kwargs)

def clean(self, value):
Expand All @@ -85,7 +95,7 @@ def clean(self, value):
return u''
value, cd = self._canon(value)
if self._calc_cd(value) != cd:
raise ValidationError(ugettext("Invalid CUIT."))
raise ValidationError(self.error_messages['checksum'])
return self._format(value, cd)

def _canon(self, cuit):
Expand Down
14 changes: 10 additions & 4 deletions django/contrib/localflavor/au/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@

class AUPostCodeField(RegexField):
"""Australian post code field."""
default_error_messages = {
'invalid': ugettext('Enter a 4 digit post code.'),
}

def __init__(self, *args, **kwargs):
super(AUPostCodeField, self).__init__(r'^\d{4}$',
max_length=None, min_length=None,
error_message=ugettext('Enter a 4 digit post code.'),
*args, **kwargs)
max_length=None, min_length=None, *args, **kwargs)

class AUPhoneNumberField(Field):
"""Australian phone number field."""
default_error_messages = {
'invalid': u'Phone numbers must contain 10 digits.',
}

def clean(self, value):
"""
Validate a phone number. Strips parentheses, whitespace and hyphens.
Expand All @@ -31,7 +37,7 @@ def clean(self, value):
phone_match = PHONE_DIGITS_RE.search(value)
if phone_match:
return u'%s' % phone_match.group(1)
raise ValidationError(u'Phone numbers must contain 10 digits.')
raise ValidationError(self.error_messages['invalid'])

class AUStateSelect(Select):
"""
Expand Down
47 changes: 32 additions & 15 deletions django/contrib/localflavor/br/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@
phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$')

class BRZipCodeField(RegexField):
default_error_messages = {
'invalid': _('Enter a zip code in the format XXXXX-XXX.'),
}

def __init__(self, *args, **kwargs):
super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$',
max_length=None, min_length=None,
error_message=_('Enter a zip code in the format XXXXX-XXX.'),
*args, **kwargs)
max_length=None, min_length=None, *args, **kwargs)

class BRPhoneNumberField(Field):
default_error_messages = {
'invalid': _('Phone numbers must be in XX-XXXX-XXXX format.'),
}

def clean(self, value):
super(BRPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
Expand All @@ -32,7 +38,7 @@ def clean(self, value):
m = phone_digits_re.search(value)
if m:
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
raise ValidationError(_('Phone numbers must be in XX-XXXX-XXXX format.'))
raise ValidationError(self.error_messages['invalid'])

class BRStateSelect(Select):
"""
Expand All @@ -48,6 +54,9 @@ class BRStateChoiceField(Field):
A choice field that uses a list of Brazilian states as its choices.
"""
widget = Select
default_error_messages = {
'invalid': _(u'Select a valid brazilian state. That state is not one of the available states.'),
}

def __init__(self, required=True, widget=None, label=None,
initial=None, help_text=None):
Expand All @@ -65,9 +74,7 @@ def clean(self, value):
return value
valid_values = set([smart_unicode(k) for k, v in self.widget.choices])
if value not in valid_values:
raise ValidationError(_(u'Select a valid brazilian state.'
u' That state is not one'
u' of the available states.'))
raise ValidationError(self.error_messages['invalid'])
return value

def DV_maker(v):
Expand All @@ -83,6 +90,12 @@ class BRCPFField(CharField):
More information:
http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas
"""
default_error_messages = {
'invalid': _("Invalid CPF number."),
'max_digits': _("This field requires at most 11 digits or 14 characters."),
'digits_only': _("This field requires only numbers."),
}

def __init__(self, *args, **kwargs):
super(BRCPFField, self).__init__(max_length=14, min_length=11, *args, **kwargs)

Expand All @@ -100,9 +113,9 @@ def clean(self, value):
try:
int(value)
except ValueError:
raise ValidationError(_("This field requires only numbers."))
raise ValidationError(self.error_messages['digits_only'])
if len(value) != 11:
raise ValidationError(_("This field requires at most 11 digits or 14 characters."))
raise ValidationError(self.error_messages['max_digits'])
orig_dv = value[-2:]

new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -1))])
Expand All @@ -112,11 +125,17 @@ def clean(self, value):
new_2dv = DV_maker(new_2dv % 11)
value = value[:-1] + str(new_2dv)
if value[-2:] != orig_dv:
raise ValidationError(_("Invalid CPF number."))
raise ValidationError(self.error_messages['invalid'])

return orig_value

class BRCNPJField(Field):
default_error_messages = {
'invalid': _("Invalid CNPJ number."),
'digits_only': _("This field requires only numbers."),
'max_digits': _("This field requires at least 14 digits"),
}

def clean(self, value):
"""
Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a
Expand All @@ -131,10 +150,9 @@ def clean(self, value):
try:
int(value)
except ValueError:
raise ValidationError("This field requires only numbers.")
raise ValidationError(self.error_messages['digits_only'])
if len(value) != 14:
raise ValidationError(
_("This field requires at least 14 digits"))
raise ValidationError(self.error_messages['max_digits'])
orig_dv = value[-2:]

new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(5, 1, -1) + range(9, 1, -1))])
Expand All @@ -144,7 +162,6 @@ def clean(self, value):
new_2dv = DV_maker(new_2dv % 11)
value = value[:-1] + str(new_2dv)
if value[-2:] != orig_dv:
raise ValidationError(_("Invalid CNPJ number."))
raise ValidationError(self.error_messages['invalid'])

return orig_value

100 changes: 57 additions & 43 deletions django/contrib/localflavor/ca/forms.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,54 @@
"""
Canada-specific Form helpers
"""
from django.newforms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.newforms.util import smart_unicode
"""
Canada-specific Form helpers
"""

from django.newforms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.newforms.util import smart_unicode
from django.utils.translation import gettext, ugettext
import re
import re

phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$')
sin_re = re.compile(r"^(\d{3})-(\d{3})-(\d{3})$")

class CAPostalCodeField(RegexField):
"""Canadian postal code field."""
def __init__(self, *args, **kwargs):
super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$',
max_length=None, min_length=None,
error_message=gettext(u'Enter a postal code in the format XXX XXX.'),
*args, **kwargs)

class CAPhoneNumberField(Field):
"""Canadian phone number field."""
def clean(self, value):
"""Validate a phone number.
"""
super(CAPhoneNumberField, self).clean(value)

class CAPostalCodeField(RegexField):
"""Canadian postal code field."""
default_error_messages = {
'invalid': gettext(u'Enter a postal code in the format XXX XXX.'),
}

def __init__(self, *args, **kwargs):
super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$',
max_length=None, min_length=None, *args, **kwargs)

class CAPhoneNumberField(Field):
"""Canadian phone number field."""
default_error_messages = {
'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
}

def clean(self, value):
"""Validate a phone number.
"""
super(CAPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
m = phone_digits_re.search(value)
if m:
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
raise ValidationError(u'Phone numbers must be in XXX-XXX-XXXX format.')
raise ValidationError(self.error_messages['invalid'])

class CAProvinceField(Field):
"""
A form field that validates its input is a Canadian province name or abbreviation.
It normalizes the input to the standard two-leter postal service
abbreviation for the given province.
"""
default_error_messages = {
'invalid': u'Enter a Canadian province or territory.',
}

def clean(self, value):
from ca_provinces import PROVINCES_NORMALIZED
super(CAProvinceField, self).clean(value)
Expand All @@ -53,17 +63,17 @@ def clean(self, value):
return PROVINCES_NORMALIZED[value.strip().lower()].decode('ascii')
except KeyError:
pass
raise ValidationError(u'Enter a Canadian province or territory.')
class CAProvinceSelect(Select):
"""
A Select widget that uses a list of Canadian provinces and
territories as its choices.
"""
def __init__(self, attrs=None):
from ca_provinces import PROVINCE_CHOICES # relative import
raise ValidationError(self.error_messages['invalid'])

class CAProvinceSelect(Select):
"""
A Select widget that uses a list of Canadian provinces and
territories as its choices.
"""
def __init__(self, attrs=None):
from ca_provinces import PROVINCE_CHOICES # relative import
super(CAProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)

class CASocialInsuranceNumberField(Field):
"""
A Canadian Social Insurance Number (SIN).
Expand All @@ -74,24 +84,28 @@ class CASocialInsuranceNumberField(Field):
* Passes the check digit process "Luhn Algorithm"
See: http://en.wikipedia.org/wiki/Social_Insurance_Number
"""
default_error_messages = {
'invalid': ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.'),
}

def clean(self, value):
super(CASocialInsuranceNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
msg = ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.')

match = re.match(sin_re, value)
if not match:
raise ValidationError(msg)
number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3))
raise ValidationError(self.error_messages['invalid'])

number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3))
check_number = u'%s%s%s' % (match.group(1), match.group(2), match.group(3))
if not self.luhn_checksum_is_valid(check_number):
raise ValidationError(msg)
raise ValidationError(self.error_messages['invalid'])
return number

def luhn_checksum_is_valid(self, number):
"""
Checks to make sure that the SIN passes a luhn mod-10 checksum
Checks to make sure that the SIN passes a luhn mod-10 checksum
See: http://en.wikipedia.org/wiki/Luhn_algorithm
"""

Expand All @@ -109,4 +123,4 @@ def luhn_checksum_is_valid(self, number):

sum = sum + digit

return ( (sum % 10) == 0 )
return ( (sum % 10) == 0 )
Loading

0 comments on commit 1fcb4e4

Please sign in to comment.