forked from dcramer/django-compositepks
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed #12379 -- Added Chinese (cn) localflavor package. Thanks, Xia K…
…ai, Daniel Duan, DaNmarner and Łukasz Rekucki. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16070 bcc190cf-cafb-0310-a4f2-bffc1f526a37
- Loading branch information
jezdez
committed
Apr 22, 2011
1 parent
facb98b
commit 6295084
Showing
8 changed files
with
441 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
""" | ||
An alphabetical list of provinces for use as `choices` in a formfield. | ||
Reference: | ||
http://en.wikipedia.org/wiki/ISO_3166-2:CN | ||
http://en.wikipedia.org/wiki/Province_%28China%29 | ||
http://en.wikipedia.org/wiki/Direct-controlled_municipality | ||
http://en.wikipedia.org/wiki/Autonomous_regions_of_China | ||
""" | ||
|
||
|
||
CN_PROVINCE_CHOICES = ( | ||
("anhui", u"安徽"), | ||
("beijing", u"北京"), | ||
("chongqing", u"重庆"), | ||
("fujian", u"福建"), | ||
("gansu", u"甘肃"), | ||
("guangdong", u"广东"), | ||
("guangxi", u"广西壮族自治区"), | ||
("guizhou", u"贵州"), | ||
("hainan", u"海南"), | ||
("hebei", u"河北"), | ||
("heilongjiang", u"黑龙江"), | ||
("henan", u"河南"), | ||
("hongkong", u"香港"), | ||
("hubei", u"湖北"), | ||
("hunan", u"湖南"), | ||
("jiangsu", u"江苏"), | ||
("jiangxi", u"江西"), | ||
("jilin", u"吉林"), | ||
("liaoning", u"辽宁"), | ||
("macao", u"澳门"), | ||
("neimongol", u"内蒙古自治区"), | ||
("ningxia", u"宁夏回族自治区"), | ||
("qinghai", u"青海"), | ||
("shaanxi", u"陕西"), | ||
("shandong", u"山东"), | ||
("shanghai", u"上海"), | ||
("shanxi", u"山西"), | ||
("sichuan", u"四川"), | ||
("taiwan", u"台湾"), | ||
("tianjin", u"天津"), | ||
("xinjiang", u"新疆维吾尔自治区"), | ||
("xizang", u"西藏自治区"), | ||
("yunnan", u"云南"), | ||
("zhejiang", u"浙江"), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
""" | ||
Chinese-specific form helpers | ||
""" | ||
import re | ||
|
||
from django.forms import ValidationError | ||
from django.forms.fields import CharField, RegexField, Select | ||
from django.utils.translation import ugettext_lazy as _ | ||
|
||
|
||
__all__ = ( | ||
'CNProvinceSelect', | ||
'CNPostCodeField', | ||
'CNIDCardField', | ||
'CNPhoneNumberField', | ||
'CNCellNumberField', | ||
) | ||
|
||
|
||
ID_CARD_RE = r'^\d{15}(\d{2}[0-9xX])?$' | ||
POST_CODE_RE = r'^\d{6}$' | ||
PHONE_RE = r'^\d{3,4}-\d{7,8}(-\d+)?$' | ||
CELL_RE = r'^1[358]\d{9}$' | ||
|
||
# Valid location code used in id card checking algorithm | ||
CN_LOCATION_CODES = ( | ||
11, # Beijing | ||
12, # Tianjin | ||
13, # Hebei | ||
14, # Shanxi | ||
15, # Nei Mongol | ||
21, # Liaoning | ||
22, # Jilin | ||
23, # Heilongjiang | ||
31, # Shanghai | ||
32, # Jiangsu | ||
33, # Zhejiang | ||
34, # Anhui | ||
35, # Fujian | ||
36, # Jiangxi | ||
37, # Shandong | ||
41, # Henan | ||
42, # Hubei | ||
43, # Hunan | ||
44, # Guangdong | ||
45, # Guangxi | ||
46, # Hainan | ||
50, # Chongqing | ||
51, # Sichuan | ||
52, # Guizhou | ||
53, # Yunnan | ||
54, # Xizang | ||
61, # Shaanxi | ||
62, # Gansu | ||
63, # Qinghai | ||
64, # Ningxia | ||
65, # Xinjiang | ||
71, # Taiwan | ||
81, # Hong Kong | ||
91, # Macao | ||
) | ||
|
||
class CNProvinceSelect(Select): | ||
""" | ||
A select widget with list of Chinese provinces as choices. | ||
""" | ||
def __init__(self, attrs=None): | ||
from cn_provinces import CN_PROVINCE_CHOICES | ||
super(CNProvinceSelect, self).__init__( | ||
attrs, choices=CN_PROVINCE_CHOICES, | ||
) | ||
|
||
|
||
class CNPostCodeField(RegexField): | ||
""" | ||
A form field that validates as Chinese post code. | ||
Valid code is XXXXXX where X is digit. | ||
""" | ||
default_error_messages = { | ||
'invalid': _(u'Enter a post code in the format XXXXXX.'), | ||
} | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(CNPostCodeField, self).__init__(POST_CODE_RE, *args, **kwargs) | ||
|
||
|
||
class CNIDCardField(CharField): | ||
""" | ||
A form field that validates as Chinese Identification Card Number. | ||
This field would check the following restrictions: | ||
* the length could only be 15 or 18. | ||
* if the length is 18, the last digit could be x or X. | ||
* has a valid checksum.(length 18 only) | ||
* has a valid birthdate. | ||
* has a valid location. | ||
The checksum algorithm is described in GB11643-1999. | ||
""" | ||
default_error_messages = { | ||
'invalid': _(u'ID Card Number consists of 15 or 18 digits.'), | ||
'checksum': _(u'Invalid ID Card Number: Wrong checksum'), | ||
'birthday': _(u'Invalid ID Card Number: Wrong birthdate'), | ||
'location': _(u'Invalid ID Card Number: Wrong location code'), | ||
} | ||
|
||
def __init__(self, max_length=18, min_length=15, *args, **kwargs): | ||
super(CNIDCardField, self).__init__(max_length, min_length, *args, | ||
**kwargs) | ||
|
||
def clean(self, value): | ||
""" | ||
Check whether the input is a valid ID Card Number. | ||
""" | ||
# Check the length of the ID card number. | ||
super(CNIDCardField, self).clean(value) | ||
if not value: | ||
return u"" | ||
# Check whether this ID card number has valid format | ||
if not re.match(ID_CARD_RE, value): | ||
raise ValidationError(self.error_messages['invalid']) | ||
# Check the birthday of the ID card number. | ||
if not self.has_valid_birthday(value): | ||
raise ValidationError(self.error_messages['birthday']) | ||
# Check the location of the ID card number. | ||
if not self.has_valid_location(value): | ||
raise ValidationError(self.error_messages['location']) | ||
# Check the checksum of the ID card number. | ||
value = value.upper() | ||
if not self.has_valid_checksum(value): | ||
raise ValidationError(self.error_messages['checksum']) | ||
return u'%s' % value | ||
|
||
def has_valid_birthday(self, value): | ||
""" | ||
This function would grab the birthdate from the ID card number and test | ||
whether it is a valid date. | ||
""" | ||
from datetime import datetime | ||
if len(value) == 15: | ||
# 1st generation ID card | ||
time_string = value[6:12] | ||
format_string = "%y%m%d" | ||
else: | ||
# 2nd generation ID card | ||
time_string = value[6:14] | ||
format_string = "%Y%m%d" | ||
try: | ||
datetime.strptime(time_string, format_string) | ||
return True | ||
except ValueError: | ||
# invalid date | ||
return False | ||
|
||
def has_valid_location(self, value): | ||
""" | ||
This method checks if the first two digits in the ID Card are valid. | ||
""" | ||
return int(value[:2]) in CN_LOCATION_CODES | ||
|
||
def has_valid_checksum(self, value): | ||
""" | ||
This method checks if the last letter/digit in value is valid | ||
according to the algorithm the ID Card follows. | ||
""" | ||
# If the length of the number is not 18, then the number is a 1st | ||
# generation ID card number, and there is no checksum to be checked. | ||
if len(value) != 18: | ||
return True | ||
checksum_index = sum( | ||
map( | ||
lambda a,b:a*(ord(b)-ord('0')), | ||
(7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2), | ||
value[:17], | ||
), | ||
) % 11 | ||
return '10X98765432'[checksum_index] == value[-1] | ||
|
||
|
||
class CNPhoneNumberField(RegexField): | ||
""" | ||
A form field that validates as Chinese phone number | ||
A valid phone number could be like: | ||
010-55555555 | ||
Considering there might be extension phone numbers, so this could also be: | ||
010-55555555-35 | ||
""" | ||
default_error_messages = { | ||
'invalid': _(u'Enter a valid phone number.'), | ||
} | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(CNPhoneNumberField, self).__init__(PHONE_RE, *args, **kwargs) | ||
|
||
|
||
class CNCellNumberField(RegexField): | ||
""" | ||
A form field that validates as Chinese cell number | ||
A valid cell number could be like: | ||
13012345678 | ||
We used a rough rule here, the first digit should be 1, the second could be | ||
3, 5 and 8, the rest could be what so ever. | ||
The length of the cell number should be 11. | ||
""" | ||
default_error_messages = { | ||
'invalid': _(u'Enter a valid cell number.'), | ||
} | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(CNCellNumberField, self).__init__(CELL_RE, *args, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.