Validates and cleans UK telephone numbers. Number length is checked, and numbers are cleaned into a common format. For example, "+44 (0)1234 567890" will be stored as "01234 567890"
Can reject premium numbers (0912 312 3123) or service numbers (1471, 118 118) with UKPhoneNumberField(reject=('premium', 'service'))
Corrects the errors found in http://djangosnippets.org/snippets/1207/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | from django.forms import ValidationError
from django.forms.fields import Field, EMPTY_VALUES
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _
import re
import types
class UKPhoneNumberField(Field):
default_error_messages = {
'partial': _('Phone number must include an area code.'),
'non_uk': _('Phone number must be a UK number.'),
'length_range': _('Phone number must be between %d and %d digits'),
'length': _('Phone number must be %d digits'),
'reject_premium': _('Phone number can\'t be premium rate.'),
'reject_service': _('Phone number can\'t be a service number.')
}
number_specs = (
(r'^01(1\d|\d1)', None, (4, 3, 4)),
(r'^016977\d{4}$', None, (6, 4)),
(r'^01(3873|5(242|39[456])|697[347]|768[347]|9467)', None, (6, 5)),
(r'^01', None, (5, (5, 6))),
(r'^0500\d{6}$', None, (4, 6)),
(r'^0(2[03489]|5[56])', None, (3, 4, 4)),
(r'^03[0347]', None, (4, 3, 4)),
(r'^07([1-57-9]|624)', None, (5, 6)),
(r'^070', 'premium', (3, 6)),
(r'^08(001111|454647)$', None, (4, 4)),
(r'^0800\d{6}$', None, (4, 6)),
(r'^08[047]\d{8}$', None, (4, 3, 4)),
(r'^09', 'premium', (4, 3, 4)),
(r'^11[68]', 'service', (3, 3)),
(r'^999$', 'service', (3,)),
(r'^1', 'service', None),
)
def __init__(self, *args, **kwargs):
self.reject = set(kwargs.pop('reject', ()))
super(UKPhoneNumberField, self).__init__(*args, **kwargs)
def clean(self, value):
super(UKPhoneNumberField, self).clean(value)
value = smart_unicode(value)
if value in EMPTY_VALUES:
return u''
value = re.sub(r'[^0-9+]', r'', value)
value = re.sub(r'(?<!^)\+', r'', value)
value = re.sub(r'^\+44(?=[1-9])', r'0', value)
value = re.sub(r'^\+44(?=0)', r'', value)
if re.match(r'^(\+(?!44)|00)', value):
raise ValidationError(self.error_messages['non_uk'])
number_spec = self.get_number_spec(value)
if not number_spec:
raise ValidationError(self.error_messages['partial'])
if number_spec[0] in self.reject:
raise ValidationError(self.error_messages['reject_%s' % number_spec[0]])
if not self.valid_length(value, number_spec):
min_length, max_length = self.spec_lengths(number_spec)
if min_length == max_length:
raise ValidationError(self.error_messages['length']
% min_length)
else:
raise ValidationError(self.error_messages['length_range']
% (min_length, max_length))
return self.format_number(value, number_spec)
def get_number_spec(self, value):
for number_spec in self.number_specs:
if re.match(number_spec[0], value):
return number_spec[1:]
return None
def spec_lengths(self, number_spec):
if not number_spec[1]:
return None, None
if type(number_spec[1][-1]) == types.TupleType:
min_length, max_length = number_spec[1][-1]
total = sum(number_spec[1][:-1])
min_length += total
max_length += total
else:
min_length = max_length = sum(number_spec[1])
return min_length, max_length
def valid_length(self, value, number_spec):
min_length, max_length = self.spec_lengths(number_spec)
if min_length is not None and len(value) < min_length: return False
if max_length is not None and len(value) > max_length: return False
return True
def format_number(self, value, number_spec):
if number_spec[1] is None:
components = (value,)
else:
components = []
position = 0
last_index = len(number_spec) - 1
for index, chunk in enumerate(number_spec[1]):
if index == last_index:
components.append(value[position:])
else:
components.append(value[position:position+chunk])
position += chunk
return ' '.join(components)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.