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)
|
Comments
A more fine-grained version of this code with more detailed RegEx patterns can be found at: http://djangosnippets.org/snippets/2810/
#