Ticket #12379: cn-localflavor-against-r11809.diff
File cn-localflavor-against-r11809.diff, 19.1 KB (added by , 15 years ago) |
---|
-
django/contrib/localflavor/cn/cn_provinces.py
1 #coding=utf-8 2 3 """ 4 An alphabetical list of provinces for use as `choices` in a formfield. 5 6 Reference: 7 http://en.wikipedia.org/wiki/ISO_3166-2:CN 8 http://en.wikipedia.org/wiki/Province_%28China%29 9 http://en.wikipedia.org/wiki/Direct-controlled_municipality 10 http://en.wikipedia.org/wiki/Autonomous_regions_of_China 11 """ 12 13 14 CN_PROVINCE_CHOICES = ( 15 ("Anhui", u"安徽"), 16 ("Beijing", u"北京"), 17 ("Chongqing", u"重庆"), 18 ("Fujian", u"福建"), 19 ("Gansu", u"甘肃"), 20 ("Guangdong", u"广东"), 21 ("Guangxi", u"广西壮族自治区"), 22 ("Guizhou", u"贵州"), 23 ("Hainan", u"海南"), 24 ("Hebei", u"河北"), 25 ("Heilongjiang", u"黑龙江"), 26 ("Henan", u"河南"), 27 ("Hong Kong", u"香港"), 28 ("Hubei", u"湖北"), 29 ("Hunan", u"湖南"), 30 ("Jiangsu", u"江苏"), 31 ("Jiangxi", u"江西"), 32 ("Jilin", u"吉林"), 33 ("Liaoning", u"辽宁"), 34 ("Macao", u"澳门"), 35 ("Nei Mongol", u"内蒙古自治区"), 36 ("Ningxia", u"宁夏回族自治区"), 37 ("Qinghai", u"青海"), 38 ("Shaanxi", u"陕西"), 39 ("Shandong", u"山东"), 40 ("Shanghai", u"上海"), 41 ("Shanxi", u"山西"), 42 ("Sichuan", u"四川"), 43 ("Taiwan", u"台湾"), 44 ("Tianjin", u"天津"), 45 ("Xinjiang", u"新疆维吾尔自治区"), 46 ("Xizang", u"西藏自治区"), 47 ("Yunnan", u"云南"), 48 ("Zhejiang", u"浙江"), 49 ) -
django/contrib/localflavor/cn/cn_location_code.py
1 #coding=utf-8 2 """ 3 This file contains a tuple of valid Chinese location codes as was 4 described in GB/T2260-1995. 5 6 Please note that this tuple is not complete and detailed. We do not want to 7 check the last four digits of the location code, which might be changed 8 (relatively) frequently. 9 """ 10 11 CN_LOCATION_CODES = ( 12 11 , # Beijing 13 12 , # Tianjin 14 13 , # Hebei 15 14 , # Shanxi 16 15 , # Nei Mongol 17 21 , # Liaoning 18 22 , # Jilin 19 23 , # Heilongjiang 20 31 , # Shanghai 21 32 , # Jiangsu 22 33 , # Zhejiang 23 34 , # Anhui 24 35 , # Fujian 25 36 , # Jiangxi 26 37 , # Shandong 27 41 , # Henan 28 42 , # Hubei 29 43 , # Hunan 30 44 , # Guangdong 31 45 , # Guangxi 32 46 , # Hainan 33 50 , # Chongqing 34 51 , # Sichuan 35 52 , # Guizhou 36 53 , # Yunnan 37 54 , # Xizang 38 61 , # Shaanxi 39 62 , # Gansu 40 63 , # Qinghai 41 64 , # Ningxia 42 65 , # Xinjiang 43 71 , # Taiwan 44 81 , # Hong Kong 45 91 , # Macao 46 ) -
django/contrib/localflavor/cn/forms.py
1 #coding=utf-8 2 """ 3 Chinese-specific form helpers 4 """ 5 import re 6 7 from django.forms import ValidationError 8 from django.forms.fields import CharField, RegexField, Select 9 from django.utils.translation import ugettext_lazy as _ 10 11 12 __all__ = ( 13 'CNProvinceSelect', 14 'CNPostCodeField', 15 'CNIDCardField', 16 'CNPhoneNumberField', 17 'CNCellNumberField', 18 ) 19 20 21 ID_CARD_RE = r'^\d{15}(\d{2}[0-9xX])?$' 22 POST_CODE_RE = r'^\d{6}$' 23 PHONE_RE = r'^\d{3,4}-\d{7,8}(-\d+)?$' 24 CELL_RE = r'^1[358]\d{9}$' 25 26 27 class CNProvinceSelect(Select): 28 """ 29 A select widget with list of Chinese provinces as choices. 30 """ 31 def __init__(self, attrs=None): 32 from cn_provinces import CN_PROVINCE_CHOICES 33 super(CNProvinceSelect, self).__init__( 34 attrs, choices=CN_PROVINCE_CHOICES, 35 ) 36 37 38 class CNPostCodeField(RegexField): 39 """ 40 A form field that validates as Chinese post code. 41 Valid code is XXXXXX where X is digit. 42 """ 43 default_error_messages = { 44 'invalid': _(u'Enter a post code in the format XXXXXX.'), 45 } 46 47 def __init__(self, *args, **kwargs): 48 super(CNPostCodeField, self).__init__(POST_CODE_RE, *args, **kwargs) 49 50 51 class CNIDCardField(CharField): 52 """ 53 A form field that validates as Chinese Identification Card Number. 54 55 This field would check the following restrictions: 56 * the length could only be 15 or 18. 57 * if the length is 18, the last digit could be x or X. 58 * has a valid checksum.(length 18 only) 59 * has a valid birthdate. 60 * has a valid location. 61 62 The checksum algorithm is described in GB11643-1999. 63 """ 64 default_error_messages = { 65 'invalid': _(u'ID Card Number consists of 15 or 18 digits.'), 66 'checksum': _(u'Invalid ID Card Number: Wrong checksum'), 67 'birthday': _(u'Invalid ID Card Number: Wrong birthdate'), 68 'location': _(u'Invalid ID Card Number: Wrong location code'), 69 } 70 71 def __init__(self, max_length=18, min_length=15, *args, **kwargs): 72 super(CNIDCardField, self).__init__(max_length, min_length, *args, 73 **kwargs) 74 75 def clean(self, value): 76 """ 77 Check whether the input is a valid ID Card Number. 78 """ 79 # Check the length of the ID card number. 80 super(CNIDCardField, self).clean(value) 81 if not value: 82 return u"" 83 # Check whether this ID card number has valid format 84 if not re.match(ID_CARD_RE, value): 85 raise ValidationError(self.error_messages['invalid']) 86 # Check the birthday of the ID card number. 87 if not self.has_valid_birthday(value): 88 raise ValidationError(self.error_messages['birthday']) 89 # Check the location of the ID card number. 90 if not self.has_valid_location(value): 91 raise ValidationError(self.error_messages['location']) 92 # Check the checksum of the ID card number. 93 value = value.upper() 94 if not self.has_valid_checksum(value): 95 raise ValidationError(self.error_messages['checksum']) 96 return u'%s' % value 97 98 def has_valid_birthday(self, value): 99 """ 100 This function would grab the birthdate from the ID card number and test 101 whether it is a valid date. 102 """ 103 from datetime import datetime 104 if len(value) == 15: 105 # 1st generation ID card 106 time_string = value[6:12] 107 format_string = "%y%m%d" 108 else: 109 # 2nd generation ID card 110 time_string = value[6:14] 111 format_string = "%Y%m%d" 112 try: 113 datetime.strptime(time_string, format_string) 114 return True 115 except ValueError: 116 # invalid date 117 return False 118 119 def has_valid_location(self, value): 120 """ 121 This function would check the first two digits in the ID card number to 122 see whether it is valid. 123 """ 124 from cn_location_code import CN_LOCATION_CODES 125 location_code = int(value[:2]) 126 return location_code in CN_LOCATION_CODES 127 128 def has_valid_checksum(self, value): 129 """ 130 This function would check whether the last digit of the ID card number 131 is a valid checksum. 132 """ 133 # If the length of the number is not 18, then the number is a 1st 134 # generation ID card number, and there is no checksum to be checked. 135 if len(value) != 18: 136 return True 137 checksum_index = sum( 138 map( 139 lambda a,b:a*(ord(b)-ord('0')), 140 (7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2), 141 value[:17], 142 ), 143 ) % 11 144 return '10X98765432'[checksum_index] == value[-1] 145 146 147 class CNPhoneNumberField(RegexField): 148 """ 149 A form field that validates as Chinese phone number 150 A valid phone number could be like: 151 010-55555555 152 Considering there might be extension phone numbers, so this could also be: 153 010-55555555-35 154 """ 155 default_error_messages = { 156 'invalid': _(u'Enter a valid phone number.'), 157 } 158 159 def __init__(self, *args, **kwargs): 160 super(CNPhoneNumberField, self).__init__(PHONE_RE, *args, **kwargs) 161 162 163 class CNCellNumberField(RegexField): 164 """ 165 A form field that validates as Chinese cell number 166 A valid cell number could be like: 167 13012345678 168 We used a rough rule here, the first digit should be 1, the second could be 169 3, 5 and 8, the rest could be what so ever. 170 The length of the cell number should be 11. 171 """ 172 default_error_messages = { 173 'invalid': _(u'Enter a valid cell number.'), 174 } 175 176 def __init__(self, *args, **kwargs): 177 super(CNCellNumberField, self).__init__(CELL_RE, *args, **kwargs) -
tests/regressiontests/forms/localflavor/cn.py
1 tests = r""" 2 3 ############################################################ 4 ##################### CNProvinceSelect ##################### 5 ############################################################ 6 >>> from django.contrib.localflavor.cn.forms import CNProvinceSelect 7 >>> s = CNProvinceSelect() 8 >>> s.render('provinces', 'Hubei') 9 u'<select name="provinces"> 10 <option value="Anhui">\u5b89\u5fbd</option> 11 <option value="Beijing">\u5317\u4eac</option> 12 <option value="Chongqing">\u91cd\u5e86</option> 13 <option value="Fujian">\u798f\u5efa</option> 14 <option value="Gansu">\u7518\u8083</option> 15 <option value="Guangdong">\u5e7f\u4e1c</option> 16 <option value="Guangxi">\u5e7f\u897f\u58ee\u65cf\u81ea\u6cbb\u533a</option> 17 <option value="Guizhou">\u8d35\u5dde</option> 18 <option value="Hainan">\u6d77\u5357</option> 19 <option value="Hebei">\u6cb3\u5317</option> 20 <option value="Heilongjiang">\u9ed1\u9f99\u6c5f</option> 21 <option value="Henan">\u6cb3\u5357</option> 22 <option value="Hong Kong">\u9999\u6e2f</option> 23 <option value="Hubei" selected="selected">\u6e56\u5317</option> 24 <option value="Hunan">\u6e56\u5357</option> 25 <option value="Jiangsu">\u6c5f\u82cf</option> 26 <option value="Jiangxi">\u6c5f\u897f</option> 27 <option value="Jilin">\u5409\u6797</option> 28 <option value="Liaoning">\u8fbd\u5b81</option> 29 <option value="Macao">\u6fb3\u95e8</option> 30 <option value="Nei Mongol">\u5185\u8499\u53e4\u81ea\u6cbb\u533a</option> 31 <option value="Ningxia">\u5b81\u590f\u56de\u65cf\u81ea\u6cbb\u533a</option> 32 <option value="Qinghai">\u9752\u6d77</option> 33 <option value="Shaanxi">\u9655\u897f</option> 34 <option value="Shandong">\u5c71\u4e1c</option> 35 <option value="Shanghai">\u4e0a\u6d77</option> 36 <option value="Shanxi">\u5c71\u897f</option> 37 <option value="Sichuan">\u56db\u5ddd</option> 38 <option value="Taiwan">\u53f0\u6e7e</option> 39 <option value="Tianjin">\u5929\u6d25</option> 40 <option value="Xinjiang">\u65b0\u7586\u7ef4\u543e\u5c14\u81ea\u6cbb\u533a</option> 41 <option value="Xizang">\u897f\u85cf\u81ea\u6cbb\u533a</option> 42 <option value="Yunnan">\u4e91\u5357</option> 43 <option value="Zhejiang">\u6d59\u6c5f</option> 44 </select>' 45 46 ############################################################ 47 ##################### CNPostCodeField ###################### 48 ############################################################ 49 >>> from django.contrib.localflavor.cn.forms import CNPostCodeField 50 >>> f = CNPostCodeField(required=False) 51 >>> f.clean('') 52 u'' 53 >>> f.clean('091209') 54 u'091209' 55 >>> f.clean('09120') 56 Traceback (most recent call last): 57 ... 58 ValidationError: [u'Enter a post code in the format XXXXXX.'] 59 >>> f.clean('09120916') 60 Traceback (most recent call last): 61 ... 62 ValidationError: [u'Enter a post code in the format XXXXXX.'] 63 64 ############################################################ 65 ##################### CNIDCardField ######################## 66 ############################################################ 67 >>> from django.contrib.localflavor.cn.forms import CNIDCardField 68 >>> f = CNIDCardField(required=False) 69 >>> f.clean('') 70 u'' 71 72 >>> # A string of 16 characters. 73 >>> f.clean("abcdefghijklmnop") 74 Traceback (most recent call last): 75 ... 76 ValidationError: [u'ID Card Number consists of 15 or 18 digits.'] 77 78 >>> # A string of 16 digits. 79 >>> f.clean("1010101010101010") 80 Traceback (most recent call last): 81 ... 82 ValidationError: [u'ID Card Number consists of 15 or 18 digits.'] 83 84 >>> # A valid 1st generation ID Card number. 85 >>> f.clean('110101491001001') 86 u'110101491001001' 87 88 >>> # A 1st generation ID Card number with invalid location 89 >>> f.clean('010101491001001') # 01, an invalid location. 90 Traceback (most recent call last): 91 ... 92 ValidationError: [u'Invalid ID Card Number: Wrong location code'] 93 94 >>> # A 1st generation ID Card number with invalid birthdate. 95 >>> f.clean('110101491041001') # 491041, an invalid day, 41 96 Traceback (most recent call last): 97 ... 98 ValidationError: [u'Invalid ID Card Number: Wrong birthdate'] 99 100 >>> # A valid 2nd generation ID Card number. 101 >>> f.clean('11010119491001001X') 102 u'11010119491001001X' 103 104 >>> # Another valid 2nd generation ID Card number, notice that the case of the last 105 >>> # character is changed. 106 >>> f.clean('11010119491001001x') 107 u'11010119491001001X' 108 >>> # An invalid 2nd generation ID Card number. 109 >>> f.clean('92010119491001001X') # 92 is an invalid location code. 110 Traceback (most recent call last): 111 ... 112 ValidationError: [u'Invalid ID Card Number: Wrong location code'] 113 114 >>> # Another invalid 2nd generation ID Card number. 115 >>> f.clean('91010119491301001X') # 19491301 is an invalid date. 116 Traceback (most recent call last): 117 ... 118 ValidationError: [u'Invalid ID Card Number: Wrong birthdate'] 119 120 >>> # Yet another invalid 2nd generation ID Card number. 121 >>> f.clean('910101194910010014') 122 Traceback (most recent call last): 123 ... 124 ValidationError: [u'Invalid ID Card Number: Wrong checksum'] 125 126 ############################################################ 127 ##################### CNPhoneNumberField ################### 128 ############################################################ 129 >>> from django.contrib.localflavor.cn.forms import CNPhoneNumberField 130 >>> f = CNPhoneNumberField(required=False) 131 >>> f.clean('') 132 u'' 133 >>> f.clean('010-12345678') 134 u'010-12345678' 135 >>> f.clean('010-1234567') 136 u'010-1234567' 137 >>> f.clean('0101-12345678') 138 u'0101-12345678' 139 >>> f.clean('0101-1234567') 140 u'0101-1234567' 141 >>> f.clean('01x-12345678') 142 Traceback (most recent call last): 143 ... 144 ValidationError: [u'Enter a valid phone number.'] 145 >>> f.clean('01123-12345678') 146 Traceback (most recent call last): 147 ... 148 ValidationError: [u'Enter a valid phone number.'] 149 >>> f.clean('010-123456789') 150 Traceback (most recent call last): 151 ... 152 ValidationError: [u'Enter a valid phone number.'] 153 >>> f.clean('010-12345678-020') 154 u'010-12345678-020' 155 >>> f.clean('010-12345678-') 156 Traceback (most recent call last): 157 ... 158 ValidationError: [u'Enter a valid phone number.'] 159 160 ############################################################ 161 ##################### CNCellNumberField #################### 162 ############################################################ 163 >>> from django.contrib.localflavor.cn.forms import CNCellNumberField 164 >>> f = CNCellNumberField(required=False) 165 >>> f.clean('') 166 u'' 167 >>> f.clean('13012345678') 168 u'13012345678' 169 >>> f.clean('130123456789') 170 Traceback (most recent call last): 171 ... 172 ValidationError: [u'Enter a valid cell number.'] 173 >>> f.clean('14012345678') 174 Traceback (most recent call last): 175 ... 176 ValidationError: [u'Enter a valid cell number.'] 177 """ -
tests/regressiontests/forms/tests.py
9 9 from localflavor.ca import tests as localflavor_ca_tests 10 10 from localflavor.ch import tests as localflavor_ch_tests 11 11 from localflavor.cl import tests as localflavor_cl_tests 12 from localflavor.cn import tests as localflavor_cn_tests 12 13 from localflavor.cz import tests as localflavor_cz_tests 13 14 from localflavor.de import tests as localflavor_de_tests 14 15 from localflavor.es import tests as localflavor_es_tests … … 44 45 'localflavor_ca_tests': localflavor_ca_tests, 45 46 'localflavor_ch_tests': localflavor_ch_tests, 46 47 'localflavor_cl_tests': localflavor_cl_tests, 48 'localflavor_cn_tests': localflavor_cn_tests, 47 49 'localflavor_cz_tests': localflavor_cz_tests, 48 50 'localflavor_de_tests': localflavor_de_tests, 49 51 'localflavor_es_tests': localflavor_es_tests, -
AUTHORS
197 197 Brant Harris 198 198 Hawkeye 199 199 Joe Heck <http://www.rhonabwy.com/wp/> 200 Xia Kai <http://blog.xiaket.org/> 200 201 Joel Heenan <joelh-django@planetjoel.com> 201 202 Mikko Hellsing <mikko@sorl.net> 202 203 Sebastian Hillig <sebastian.hillig@gmail.com> -
docs/ref/contrib/localflavor.txt
44 44 * Brazil_ 45 45 * Canada_ 46 46 * Chile_ 47 * China_ 47 48 * Czech_ 48 49 * Finland_ 49 50 * France_ … … 84 85 .. _Brazil: `Brazil (br)`_ 85 86 .. _Canada: `Canada (ca)`_ 86 87 .. _Chile: `Chile (cl)`_ 88 .. _China: `China (cn)`_ 87 89 .. _Czech: `Czech (cz)`_ 88 90 .. _Finland: `Finland (fi)`_ 89 91 .. _France: `France (fr)`_ … … 233 235 A ``Select`` widget that uses a list of Chilean regions (Regiones) as its 234 236 choices. 235 237 238 China (``cn``) 239 ============== 240 241 .. class:: cn.forms.CNProvinceSelect 242 243 A ``Select`` widget that uses a list of Chinese regions as its choices. 244 245 .. class:: cn.forms.CNPostCodeField 246 247 A form field that validates input as a Chinese post code. 248 Valid formats are XXXXXX where X is digit. 249 250 .. class:: cn.forms.CNIDCardField 251 252 A form field that validates input as a Chinese Identification Card Number. 253 Both 1st and 2nd generation ID Card Number are validated. 254 255 .. class:: cn.forms.CNPhoneNumberField 256 257 A form field that validates input as a Chinese phone number. 258 Valid formats are 0XX-XXXXXXXX, composed of 3 or 4 digits of region code 259 and 7 or 8 digits of phone number. 260 261 .. class:: cn.forms.CNCellNumberField 262 263 A form field that validates input as a Chinese mobile phone number. 264 Valid formats are like 1XXXXXXXXXX, where X is digit. 265 The second digit could only be 3, 5 and 8. 266 236 267 Czech (``cz``) 237 268 ============== 238 269