Ticket #17591: us_ssnfield_fix.patch
File us_ssnfield_fix.patch, 6.9 KB (added by , 13 years ago) |
---|
-
django/contrib/localflavor/us/forms.py
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8
15 15 16 16 phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$') 17 17 ssn_re = re.compile(r"^(?P<area>\d{3})[-\ ]?(?P<group>\d{2})[-\ ]?(?P<serial>\d{4})$") 18 zeros_re = re.compile('^0+$') 18 19 19 20 class USZipCodeField(RegexField): 20 21 default_error_messages = { … … 55 56 promotional use or distribution (e.g., the Woolworth's number or the 56 57 1962 promotional number). 57 58 """ 59 58 60 default_error_messages = { 59 61 'invalid': _('Enter a valid U.S. Social Security number in XXX-XX-XXXX format.'), 60 62 } 61 63 64 def __init__(self, *args, **kwargs): 65 self.no_hyphens = kwargs.pop("no_hyphens", False) 66 super(USSocialSecurityNumberField, self).__init__(*args, **kwargs) 67 62 68 def clean(self, value): 63 super(USSocialSecurityNumberField, self).clean(value)69 value = super(USSocialSecurityNumberField, self).clean(value) 64 70 if value in EMPTY_VALUES: 65 return u'' 66 match = re.match(ssn_re, value) 67 if not match: 68 raise ValidationError(self.error_messages['invalid']) 69 area, group, serial = match.groupdict()['area'], match.groupdict()['group'], match.groupdict()['serial'] 71 return '' 70 72 71 # First pass: no blocks of all zeroes. 72 if area == '000' or \ 73 group == '00' or \ 74 serial == '0000': 75 raise ValidationError(self.error_messages['invalid']) 73 ssn_parts = self._get_ssn_parts(value) 74 if self._is_invalid_ssn(ssn_parts): 75 self._raise_invalid() 76 return self._get_formatted_ssn(ssn_parts) 76 77 77 # Second pass: promotional and otherwise permanently invalid numbers. 78 if area == '666' or \ 79 (area == '987' and group == '65' and 4320 <= int(serial) <= 4329) or \ 80 value == '078-05-1120' or \ 81 value == '219-09-9999': 82 raise ValidationError(self.error_messages['invalid']) 83 return u'%s-%s-%s' % (area, group, serial) 78 def _is_invalid_ssn(self, ssn_parts): 79 return any([ 80 self._has_zero_blocks(**ssn_parts), 81 self._is_invalid_area(ssn_parts['area']), 82 self._is_in_promotional_range(**ssn_parts), 83 self._is_known_invalid(**ssn_parts), 84 ]) 85 86 def _get_formatted_ssn(self, ssn_parts): 87 ssn = "{area}-{group}-{serial}".format(**ssn_parts) 88 if self.no_hyphens: 89 ssn = ssn.replace('-', '') 90 return ssn 91 92 def _get_ssn_parts(self, value): 93 m = ssn_re.match(value) 94 if not m: 95 self._raise_invalid() 96 return m.groupdict() 97 98 def _raise_invalid(self): 99 raise ValidationError(self.error_messages['invalid']) 100 101 def _has_zero_blocks(self, area, group, serial): 102 return any([self._is_all_zeros(p) for p in (area, group, serial)]) 103 104 def _is_all_zeros(self, part): 105 return zeros_re.match(part) 106 107 def _is_invalid_area(self, area): 108 return bool(area == '666') 109 110 def _is_in_promotional_range(self, area, group, serial): 111 return bool(area == '987' and group == '65' and 4320 <= int(serial) <= 4329) 112 113 def _is_known_invalid(self, area, group, serial): 114 return any([ 115 bool(area == '078' and group == '05' and serial == '1120'), 116 bool(area == '219' and group == '09' and serial == '9999'), 117 ]) 84 118 85 119 class USStateField(Field): 86 120 """ -
tests/regressiontests/localflavor/us/tests.py
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8
4 4 USPhoneNumberField, USStateField, USStateSelect, 5 5 USSocialSecurityNumberField) 6 6 from django.test import SimpleTestCase 7 from django.forms import ValidationError 7 8 8 9 from .forms import USPlaceForm 9 10 … … 11 12 class USLocalFlavorTests(SimpleTestCase): 12 13 13 14 def setUp(self): 15 self.ssn_field = USSocialSecurityNumberField() 14 16 self.form = USPlaceForm({'state':'GA', 'state_req':'NC', 'postal_code': 'GA', 'name':'impossible'}) 15 17 16 18 def test_get_display_methods(self): … … 278 280 279 281 valid = { 280 282 '987-65-4330': '987-65-4330', 281 '987654330': '987-65-4330',283 '987654330': '987-65-4330', 282 284 } 283 285 invalid = { 284 286 '078-05-1120': error_invalid, 287 '078051120': error_invalid, 288 '219-09-9999': error_invalid, 289 '219099999': error_invalid, 290 '000-12-1234': error_invalid, 291 '123-00-1234': error_invalid, 292 '123-12-0000': error_invalid, 285 293 } 286 294 self.assertFieldOutput(USSocialSecurityNumberField, valid, invalid) 295 296 def test_returns_value_when_valid_social_security_number(self): 297 ssn = "123-45-6789" 298 self.assertEqual(ssn, self.ssn_field.clean(ssn)) 299 300 def test_forces_number_to_dashes_when_valid_and_not_remove_dashes(self): 301 ssn = "123456789" 302 self.assertEqual("123-45-6789", self.ssn_field.clean(ssn)) 303 304 def test_forces_number_to_no_dashes_when_valid_and_remove_dashes(self): 305 field = USSocialSecurityNumberField(no_hyphens=True) 306 ssn = "123-45-6789" 307 self.assertEqual("123456789", field.clean(ssn)) 308 309 def test_is_not_valid_when_any_blocks_are_all_zero(self): 310 with self.assertRaises(ValidationError): 311 self.ssn_field.clean("000-12-1234") 312 313 with self.assertRaises(ValidationError): 314 self.ssn_field.clean("123-00-1234") 315 316 with self.assertRaises(ValidationError): 317 self.ssn_field.clean("123-12-0000") 318 319 def test_is_not_valid_when_area_is_666(self): 320 with self.assertRaises(ValidationError): 321 self.ssn_field.clean("666-12-1234") 322 323 def test_is_not_valid_when_in_promotional_range(self): 324 # 987-65-(4320 - 4329) is invalid 325 for x in range(4320, 4330): 326 with self.assertRaises(ValidationError): 327 self.ssn_field.clean("987-65-{area}".format(area=x)) 328 self.assertEqual("987-65-4330", self.ssn_field.clean("987-65-4330")) 329 330 def test_078_05_1120_is_invalid(self): 331 with self.assertRaises(ValidationError): 332 self.ssn_field.clean("078-05-1120") 333 334 def test_219_09_9999_is_invalid(self): 335 with self.assertRaises(ValidationError): 336 self.ssn_field.clean("219-09-9999")