mirror of https://github.com/django/django.git
Fixed #16497 -- Added new form and model fields to the Mexican local flavor. Many thanks to Andrés Torres Marroquín and Gerardo Orozco.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16572 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
4a6e1b5613
commit
0fbadfd1c8
2
AUTHORS
2
AUTHORS
|
@ -340,6 +340,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Nuno Mariz <nmariz@gmail.com>
|
Nuno Mariz <nmariz@gmail.com>
|
||||||
mark@junklight.com
|
mark@junklight.com
|
||||||
Orestis Markou <orestis@orestis.gr>
|
Orestis Markou <orestis@orestis.gr>
|
||||||
|
Andrés Torres Marroquín <andres.torres.marroquin@gmail.com>
|
||||||
Takashi Matsuo <matsuo.takashi@gmail.com>
|
Takashi Matsuo <matsuo.takashi@gmail.com>
|
||||||
Zlatko Mašek <zlatko.masek@gmail.com>
|
Zlatko Mašek <zlatko.masek@gmail.com>
|
||||||
Yasushi Masuda <whosaysni@gmail.com>
|
Yasushi Masuda <whosaysni@gmail.com>
|
||||||
|
@ -380,6 +381,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Neal Norwitz <nnorwitz@google.com>
|
Neal Norwitz <nnorwitz@google.com>
|
||||||
Todd O'Bryan <toddobryan@mac.com>
|
Todd O'Bryan <toddobryan@mac.com>
|
||||||
Selwin Ong <selwin@ui.co.id>
|
Selwin Ong <selwin@ui.co.id>
|
||||||
|
Gerardo Orozco <gerardo.orozco.mosqueda@gmail.com>
|
||||||
Christian Oudard <christian.oudard@gmail.com>
|
Christian Oudard <christian.oudard@gmail.com>
|
||||||
oggie rob <oz.robharvey@gmail.com>
|
oggie rob <oz.robharvey@gmail.com>
|
||||||
oggy <ognjen.maric@gmail.com>
|
oggy <ognjen.maric@gmail.com>
|
||||||
|
|
|
@ -1,14 +1,225 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Mexican-specific form helpers.
|
Mexican-specific form helpers.
|
||||||
"""
|
"""
|
||||||
|
import re
|
||||||
|
|
||||||
from django.forms.fields import Select
|
from django.forms import ValidationError
|
||||||
|
from django.forms.fields import Select, RegexField
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.core.validators import EMPTY_VALUES
|
||||||
|
from django.contrib.localflavor.mx.mx_states import STATE_CHOICES
|
||||||
|
|
||||||
|
DATE_RE = r'\d{2}((01|03|05|07|08|10|12)(0[1-9]|[12]\d|3[01])|02(0[1-9]|[12]\d)|(04|06|09|11)(0[1-9]|[12]\d|30))'
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is the list of inconvenient words according to the `Anexo IV` of the
|
||||||
|
document described in the next link:
|
||||||
|
http://www.sisi.org.mx/jspsi/documentos/2005/seguimiento/06101/0610100162005_065.doc
|
||||||
|
"""
|
||||||
|
|
||||||
|
RFC_INCONVENIENT_WORDS = [
|
||||||
|
u'BUEI', u'BUEY', u'CACA', u'CACO', u'CAGA', u'CAGO', u'CAKA', u'CAKO',
|
||||||
|
u'COGE', u'COJA', u'COJE', u'COJI', u'COJO', u'CULO', u'FETO', u'GUEY',
|
||||||
|
u'JOTO', u'KACA', u'KACO', u'KAGA', u'KAGO', u'KOGE', u'KOJO', u'KAKA',
|
||||||
|
u'KULO', u'MAME', u'MAMO', u'MEAR', u'MEAS', u'MEON', u'MION', u'MOCO',
|
||||||
|
u'MULA', u'PEDA', u'PEDO', u'PENE', u'PUTA', u'PUTO', u'QULO', u'RATA',
|
||||||
|
u'RUIN',
|
||||||
|
]
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is the list of inconvenient words according to the `Anexo 2` of the
|
||||||
|
document described in the next link:
|
||||||
|
http://portal.veracruz.gob.mx/pls/portal/url/ITEM/444112558A57C6E0E040A8C02E00695C
|
||||||
|
"""
|
||||||
|
CURP_INCONVENIENT_WORDS = [
|
||||||
|
u'BACA', u'BAKA', u'BUEI', u'BUEY', u'CACA', u'CACO', u'CAGA', u'CAGO',
|
||||||
|
u'CAKA', u'CAKO', u'COGE', u'COGI', u'COJA', u'COJE', u'COJI', u'COJO',
|
||||||
|
u'COLA', u'CULO', u'FALO', u'FETO', u'GETA', u'GUEI', u'GUEY', u'JETA',
|
||||||
|
u'JOTO', u'KACA', u'KACO', u'KAGA', u'KAGO', u'KAKA', u'KAKO', u'KOGE',
|
||||||
|
u'KOGI', u'KOJA', u'KOJE', u'KOJI', u'KOJO', u'KOLA', u'KULO', u'LILO',
|
||||||
|
u'LOCA', u'LOCO', u'LOKA', u'LOKO', u'MAME', u'MAMO', u'MEAR', u'MEAS',
|
||||||
|
u'MEON', u'MIAR', u'MION', u'MOCO', u'MOKO', u'MULA', u'MULO', u'NACA',
|
||||||
|
u'NACO', u'PEDA', u'PEDO', u'PENE', u'PIPI', u'PITO', u'POPO', u'PUTA',
|
||||||
|
u'PUTO', u'QULO', u'RATA', u'ROBA', u'ROBE', u'ROBO', u'RUIN', u'SENO',
|
||||||
|
u'TETA', u'VACA', u'VAGA', u'VAGO', u'VAKA', u'VUEI', u'VUEY', u'WUEI',
|
||||||
|
u'WUEY',
|
||||||
|
]
|
||||||
|
|
||||||
class MXStateSelect(Select):
|
class MXStateSelect(Select):
|
||||||
"""
|
"""
|
||||||
A Select widget that uses a list of Mexican states as its choices.
|
A Select widget that uses a list of Mexican states as its choices.
|
||||||
"""
|
"""
|
||||||
def __init__(self, attrs=None):
|
def __init__(self, attrs=None):
|
||||||
from mx_states import STATE_CHOICES
|
|
||||||
super(MXStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
|
super(MXStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
|
||||||
|
|
||||||
|
|
||||||
|
class MXZipCodeField(RegexField):
|
||||||
|
"""
|
||||||
|
A form field that accepts a Mexican Zip Code.
|
||||||
|
|
||||||
|
More info about this:
|
||||||
|
http://en.wikipedia.org/wiki/List_of_postal_codes_in_Mexico
|
||||||
|
"""
|
||||||
|
default_error_messages = {
|
||||||
|
'invalid': _(u'Enter a valid zip code in the format XXXXX.'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
zip_code_re = ur'^(0[1-9]|[1][0-6]|[2-9]\d)(\d{3})$'
|
||||||
|
super(MXZipCodeField, self).__init__(zip_code_re, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MXRFCField(RegexField):
|
||||||
|
"""
|
||||||
|
A form field that validates a Mexican *Registro Federal de Contribuyentes*
|
||||||
|
for either `Persona física` or `Persona moral`.
|
||||||
|
|
||||||
|
The Persona física RFC string is integrated by a juxtaposition of
|
||||||
|
characters following the next pattern:
|
||||||
|
|
||||||
|
===== ====== ===========================================
|
||||||
|
Index Format Accepted Characters
|
||||||
|
===== ====== ===========================================
|
||||||
|
1 X Any letter
|
||||||
|
2 X Any vowel
|
||||||
|
3-4 XX Any letter
|
||||||
|
5-10 YYMMDD Any valid date
|
||||||
|
11-12 XX Any letter or number between 0 and 9
|
||||||
|
13 X Any digit between 0 and 9 or the letter *A*
|
||||||
|
===== ====== ===========================================
|
||||||
|
|
||||||
|
The Persona moral RFC string is integrated by a juxtaposition of
|
||||||
|
characters following the next pattern:
|
||||||
|
|
||||||
|
===== ====== ============================================
|
||||||
|
Index Format Accepted Characters
|
||||||
|
===== ====== ============================================
|
||||||
|
1-3 XXX Any letter including *&* and *Ñ* chars
|
||||||
|
4-9 YYMMDD Any valid date
|
||||||
|
10-11 XX Any letter or number between 0 and 9
|
||||||
|
12 X Any number between 0 and 9 or the letter *A*
|
||||||
|
===== ====== ============================================
|
||||||
|
|
||||||
|
More info about this:
|
||||||
|
http://es.wikipedia.org/wiki/Registro_Federal_de_Contribuyentes_(M%C3%A9xico)
|
||||||
|
"""
|
||||||
|
default_error_messages = {
|
||||||
|
'invalid': _('Enter a valid RFC.'),
|
||||||
|
'invalid_checksum': _('Invalid checksum for RFC.'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, min_length=9, max_length=13, *args, **kwargs):
|
||||||
|
rfc_re = re.compile(ur'^([A-Z&Ññ]{3}|[A-Z][AEIOU][A-Z]{2})%s([A-Z0-9]{2}[0-9A])?$' % DATE_RE,
|
||||||
|
re.IGNORECASE)
|
||||||
|
super(MXRFCField, self).__init__(rfc_re, min_length=min_length,
|
||||||
|
max_length=max_length, *args, **kwargs)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
value = super(MXRFCField, self).clean(value)
|
||||||
|
if value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
|
value = value.upper()
|
||||||
|
if self._has_homoclave(value):
|
||||||
|
if not value[-1] == self._checksum(value[:-1]):
|
||||||
|
raise ValidationError(self.default_error_messages['invalid_checksum'])
|
||||||
|
if self._has_inconvenient_word(value):
|
||||||
|
raise ValidationError(self.default_error_messages['invalid'])
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _has_homoclave(self, rfc):
|
||||||
|
"""
|
||||||
|
This check is done due to the existance of RFCs without a *homoclave*
|
||||||
|
since the current algorithm to calculate it had not been created for
|
||||||
|
the first RFCs ever in Mexico.
|
||||||
|
"""
|
||||||
|
rfc_without_homoclave_re = re.compile(ur'^[A-Z&Ññ]{3,4}%s$' % DATE_RE,
|
||||||
|
re.IGNORECASE)
|
||||||
|
return not rfc_without_homoclave_re.match(rfc)
|
||||||
|
|
||||||
|
def _checksum(self, rfc):
|
||||||
|
"""
|
||||||
|
More info about this procedure:
|
||||||
|
www.sisi.org.mx/jspsi/documentos/2005/seguimiento/06101/0610100162005_065.doc
|
||||||
|
"""
|
||||||
|
chars = u'0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ-Ñ'
|
||||||
|
if len(rfc) is 11:
|
||||||
|
rfc = '-' + rfc
|
||||||
|
|
||||||
|
sum_ = sum(i * chars.index(c) for i, c in zip(reversed(xrange(14)), rfc))
|
||||||
|
checksum = 11 - sum_ % 11
|
||||||
|
|
||||||
|
if checksum == 10:
|
||||||
|
return u'A'
|
||||||
|
elif checksum == 11:
|
||||||
|
return u'0'
|
||||||
|
|
||||||
|
return unicode(checksum)
|
||||||
|
|
||||||
|
def _has_inconvenient_word(self, rfc):
|
||||||
|
first_four = rfc[:4]
|
||||||
|
return first_four in RFC_INCONVENIENT_WORDS
|
||||||
|
|
||||||
|
|
||||||
|
class MXCURPField(RegexField):
|
||||||
|
"""
|
||||||
|
A field that validates a Mexican Clave Única de Registro de Población.
|
||||||
|
|
||||||
|
The CURP is integrated by a juxtaposition of characters following the next
|
||||||
|
pattern:
|
||||||
|
|
||||||
|
===== ====== ===================================================
|
||||||
|
Index Format Accepted Characters
|
||||||
|
===== ====== ===================================================
|
||||||
|
1 X Any letter
|
||||||
|
2 X Any vowel
|
||||||
|
3-4 XX Any letter
|
||||||
|
5-10 YYMMDD Any valid date
|
||||||
|
11 X Either `H` or `M`, depending on the person's gender
|
||||||
|
12-13 XX Any valid acronym for a state in Mexico
|
||||||
|
14-16 XXX Any consonant
|
||||||
|
17 X Any number between 0 and 9 or any letter
|
||||||
|
18 X Any number between 0 and 9
|
||||||
|
===== ====== ===================================================
|
||||||
|
|
||||||
|
More info about this:
|
||||||
|
http://www.condusef.gob.mx/index.php/clave-unica-de-registro-de-poblacion-curp
|
||||||
|
"""
|
||||||
|
default_error_messages = {
|
||||||
|
'invalid': _('Enter a valid CURP.'),
|
||||||
|
'invalid_checksum': _(u'Invalid checksum for CURP.'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, min_length=18, max_length=18, *args, **kwargs):
|
||||||
|
states_re = r'(AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS|NE)'
|
||||||
|
consonants_re = r'[B-DF-HJ-NP-TV-Z]'
|
||||||
|
curp_re = (ur'^[A-Z][AEIOU][A-Z]{2}%s[HM]%s%s{3}[0-9A-Z]\d$' %
|
||||||
|
(DATE_RE, states_re, consonants_re))
|
||||||
|
curp_re = re.compile(curp_re, re.IGNORECASE)
|
||||||
|
super(MXCURPField, self).__init__(curp_re, min_length=min_length,
|
||||||
|
max_length=max_length, *args, **kwargs)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
value = super(MXCURPField, self).clean(value)
|
||||||
|
if value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
|
value = value.upper()
|
||||||
|
if value[-1] != self._checksum(value[:-1]):
|
||||||
|
raise ValidationError(self.default_error_messages['invalid_checksum'])
|
||||||
|
if self._has_inconvenient_word(value):
|
||||||
|
raise ValidationError(self.default_error_messages['invalid'])
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _checksum(self, value):
|
||||||
|
chars = u'0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ'
|
||||||
|
|
||||||
|
s = sum(i * chars.index(c) for i, c in zip(reversed(xrange(19)), value))
|
||||||
|
checksum = 10 - s % 10
|
||||||
|
|
||||||
|
if checksum == 10:
|
||||||
|
return u'0'
|
||||||
|
return unicode(checksum)
|
||||||
|
|
||||||
|
def _has_inconvenient_word(self, curp):
|
||||||
|
first_four = curp[:4]
|
||||||
|
return first_four in CURP_INCONVENIENT_WORDS
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.db.models.fields import CharField
|
||||||
|
|
||||||
|
from django.contrib.localflavor.mx.mx_states import STATE_CHOICES
|
||||||
|
from django.contrib.localflavor.mx.forms import (MXRFCField as MXRFCFormField,
|
||||||
|
MXZipCodeField as MXZipCodeFormField, MXCURPField as MXCURPFormField)
|
||||||
|
|
||||||
|
|
||||||
|
class MXStateField(CharField):
|
||||||
|
"""
|
||||||
|
A model field that stores the three-letter Mexican state abbreviation in the
|
||||||
|
database.
|
||||||
|
"""
|
||||||
|
description = _("Mexico state (three uppercase letters)")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['choices'] = STATE_CHOICES
|
||||||
|
kwargs['max_length'] = 3
|
||||||
|
super(MXStateField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MXZipCodeField(CharField):
|
||||||
|
"""
|
||||||
|
A model field that forms represent as a forms.MXZipCodeField field and
|
||||||
|
stores the five-digit Mexican zip code.
|
||||||
|
"""
|
||||||
|
description = _("Mexico zip code")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['max_length'] = 5
|
||||||
|
super(MXZipCodeField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'form_class': MXZipCodeFormField}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return super(MXZipCodeField, self).formfield(**defaults)
|
||||||
|
|
||||||
|
|
||||||
|
class MXRFCField(CharField):
|
||||||
|
"""
|
||||||
|
A model field that forms represent as a forms.MXRFCField field and
|
||||||
|
stores the value of a valid Mexican RFC.
|
||||||
|
"""
|
||||||
|
description = _("Mexican RFC")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['max_length'] = 13
|
||||||
|
super(MXRFCField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'form_class': MXRFCFormField}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return super(MXRFCField, self).formfield(**defaults)
|
||||||
|
|
||||||
|
|
||||||
|
class MXCURPField(CharField):
|
||||||
|
"""
|
||||||
|
A model field that forms represent as a forms.MXCURPField field and
|
||||||
|
stores the value of a valid Mexican CURP.
|
||||||
|
"""
|
||||||
|
description = _("Mexican CURP")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['max_length'] = 18
|
||||||
|
super(MXCURPField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'form_class': MXCURPFormField}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return super(MXCURPField, self).formfield(**defaults)
|
|
@ -8,6 +8,7 @@ when explicitly needed.
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
# All 31 states, plus the `Distrito Federal`.
|
||||||
STATE_CHOICES = (
|
STATE_CHOICES = (
|
||||||
('AGU', _(u'Aguascalientes')),
|
('AGU', _(u'Aguascalientes')),
|
||||||
('BCN', _(u'Baja California')),
|
('BCN', _(u'Baja California')),
|
||||||
|
@ -42,4 +43,3 @@ STATE_CHOICES = (
|
||||||
('YUC', _(u'Yucatán')),
|
('YUC', _(u'Yucatán')),
|
||||||
('ZAC', _(u'Zacatecas')),
|
('ZAC', _(u'Zacatecas')),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -798,10 +798,79 @@ Macedonia (``mk``)
|
||||||
Mexico (``mx``)
|
Mexico (``mx``)
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
.. class:: mx.forms.MXZipCodeField
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
A form field that accepts a Mexican Zip Code.
|
||||||
|
|
||||||
|
More info about this: List of postal codes in Mexico (zipcodes_)
|
||||||
|
|
||||||
|
.. _zipcodes: http://en.wikipedia.org/wiki/List_of_postal_codes_in_Mexico
|
||||||
|
|
||||||
|
.. class:: mx.forms.MXRFCField
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
A form field that validates a Mexican *Registro Federal de Contribuyentes* for
|
||||||
|
either **Persona física** or **Persona moral**. This field accepts RFC strings
|
||||||
|
whether or not it contains a *homoclave*.
|
||||||
|
|
||||||
|
More info about this: Registro Federal de Contribuyentes (rfc_)
|
||||||
|
|
||||||
|
.. _rfc: http://es.wikipedia.org/wiki/Registro_Federal_de_Contribuyentes_(M%C3%A9xico)
|
||||||
|
|
||||||
|
.. class:: mx.forms.MXCURPField
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
A field that validates a Mexican *Clave Única de Registro de Población*.
|
||||||
|
|
||||||
|
More info about this: Clave Unica de Registro de Poblacion (curp_)
|
||||||
|
|
||||||
|
.. _curp: http://www.condusef.gob.mx/index.php/clave-unica-de-registro-de-poblacion-curp
|
||||||
|
|
||||||
.. class:: mx.forms.MXStateSelect
|
.. class:: mx.forms.MXStateSelect
|
||||||
|
|
||||||
A ``Select`` widget that uses a list of Mexican states as its choices.
|
A ``Select`` widget that uses a list of Mexican states as its choices.
|
||||||
|
|
||||||
|
.. class:: mx.models.MXStateField
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
A model field that stores the three-letter Mexican state abbreviation in the
|
||||||
|
database.
|
||||||
|
|
||||||
|
.. class:: mx.models.MXZipCodeField
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
A model field that forms represent as a ``forms.MXZipCodeField`` field and
|
||||||
|
stores the five-digit Mexican zip code.
|
||||||
|
|
||||||
|
.. class:: mx.models.MXRFCField
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
A model field that forms represent as a ``forms.MXRFCField`` field and
|
||||||
|
stores the value of a valid Mexican RFC.
|
||||||
|
|
||||||
|
.. class:: mx.models.MXCURPField
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
A model field that forms represent as a ``forms.MXCURPField`` field and
|
||||||
|
stores the value of a valid Mexican CURP.
|
||||||
|
|
||||||
|
Additionally, a choice tuple is provided in ``django.contrib.localflavor.mx.mx_states``,
|
||||||
|
allowing customized model and form fields, and form presentations, for subsets of
|
||||||
|
Mexican states abbreviations:
|
||||||
|
|
||||||
|
.. data:: mx.mx_states.STATE_CHOICES
|
||||||
|
|
||||||
|
A tuple of choices of the states abbreviations for all 31 Mexican states,
|
||||||
|
plus the `Distrito Federal`.
|
||||||
|
|
||||||
Norway (``no``)
|
Norway (``no``)
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from django.contrib.localflavor.mx.forms import (MXZipCodeField, MXRFCField,
|
||||||
|
MXStateSelect, MXCURPField)
|
||||||
|
|
||||||
|
from utils import LocalFlavorTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class MXLocalFlavorTests(LocalFlavorTestCase):
|
||||||
|
def test_MXStateSelect(self):
|
||||||
|
f = MXStateSelect()
|
||||||
|
out = u'''<select name="state">
|
||||||
|
<option value="AGU">Aguascalientes</option>
|
||||||
|
<option value="BCN">Baja California</option>
|
||||||
|
<option value="BCS">Baja California Sur</option>
|
||||||
|
<option value="CAM">Campeche</option>
|
||||||
|
<option value="CHH">Chihuahua</option>
|
||||||
|
<option value="CHP">Chiapas</option>
|
||||||
|
<option value="COA">Coahuila</option>
|
||||||
|
<option value="COL">Colima</option>
|
||||||
|
<option value="DIF">Distrito Federal</option>
|
||||||
|
<option value="DUR">Durango</option>
|
||||||
|
<option value="GRO">Guerrero</option>
|
||||||
|
<option value="GUA">Guanajuato</option>
|
||||||
|
<option value="HID">Hidalgo</option>
|
||||||
|
<option value="JAL">Jalisco</option>
|
||||||
|
<option value="MEX">Estado de México</option>
|
||||||
|
<option value="MIC" selected="selected">Michoacán</option>
|
||||||
|
<option value="MOR">Morelos</option>
|
||||||
|
<option value="NAY">Nayarit</option>
|
||||||
|
<option value="NLE">Nuevo León</option>
|
||||||
|
<option value="OAX">Oaxaca</option>
|
||||||
|
<option value="PUE">Puebla</option>
|
||||||
|
<option value="QUE">Querétaro</option>
|
||||||
|
<option value="ROO">Quintana Roo</option>
|
||||||
|
<option value="SIN">Sinaloa</option>
|
||||||
|
<option value="SLP">San Luis Potosí</option>
|
||||||
|
<option value="SON">Sonora</option>
|
||||||
|
<option value="TAB">Tabasco</option>
|
||||||
|
<option value="TAM">Tamaulipas</option>
|
||||||
|
<option value="TLA">Tlaxcala</option>
|
||||||
|
<option value="VER">Veracruz</option>
|
||||||
|
<option value="YUC">Yucatán</option>
|
||||||
|
<option value="ZAC">Zacatecas</option>
|
||||||
|
</select>'''
|
||||||
|
self.assertEqual(f.render('state', 'MIC'), out)
|
||||||
|
|
||||||
|
def test_MXZipCodeField(self):
|
||||||
|
error_format = [u'Enter a valid zip code in the format XXXXX.']
|
||||||
|
valid = {
|
||||||
|
'58120': u'58120',
|
||||||
|
'58502': u'58502',
|
||||||
|
'59310': u'59310',
|
||||||
|
'99999': u'99999',
|
||||||
|
}
|
||||||
|
invalid = {
|
||||||
|
'17000': error_format,
|
||||||
|
'18000': error_format,
|
||||||
|
'19000': error_format,
|
||||||
|
'00000': error_format,
|
||||||
|
}
|
||||||
|
self.assertFieldOutput(MXZipCodeField, valid, invalid)
|
||||||
|
|
||||||
|
def test_MXRFCField(self):
|
||||||
|
error_format = [u'Enter a valid RFC.']
|
||||||
|
error_checksum = [u'Invalid checksum for RFC.']
|
||||||
|
valid = {
|
||||||
|
'MoFN641205eX5': u'MOFN641205EX5',
|
||||||
|
'ICa060120873': u'ICA060120873',
|
||||||
|
'eUcG751104rT0': u'EUCG751104RT0',
|
||||||
|
'GME08100195A': u'GME08100195A',
|
||||||
|
'AA&060524KX5': u'AA&060524KX5',
|
||||||
|
'CAÑ0708045P7': u'CAÑ0708045P7',
|
||||||
|
'aaa000101aa9': u'AAA000101AA9',
|
||||||
|
}
|
||||||
|
invalid = {
|
||||||
|
'MED0000000XA': error_format,
|
||||||
|
'0000000000XA': error_format,
|
||||||
|
'AAA000000AA6': error_format,
|
||||||
|
# Dates
|
||||||
|
'XXX880002XXX': error_format,
|
||||||
|
'XXX880200XXX': error_format,
|
||||||
|
'XXX880132XXX': error_format,
|
||||||
|
'XXX880230XXX': error_format,
|
||||||
|
'XXX880431XXX': error_format,
|
||||||
|
# Incorrect checksum
|
||||||
|
'MOGR650524E73': error_checksum,
|
||||||
|
'HVA7810058F1': error_checksum,
|
||||||
|
'MoFN641205eX2': error_checksum,
|
||||||
|
'ICa060120871': error_checksum,
|
||||||
|
'eUcG751104rT7': error_checksum,
|
||||||
|
'GME081001955': error_checksum,
|
||||||
|
'AA&060524KX9': error_checksum,
|
||||||
|
'CAÑ0708045P2': error_checksum,
|
||||||
|
}
|
||||||
|
self.assertFieldOutput(MXRFCField, valid, invalid)
|
||||||
|
|
||||||
|
def test_MXCURPField(self):
|
||||||
|
error_format = [u'Enter a valid CURP.']
|
||||||
|
error_checksum = [u'Invalid checksum for CURP.']
|
||||||
|
valid = {
|
||||||
|
'AaMG890608HDFLJL00': u'AAMG890608HDFLJL00',
|
||||||
|
'BAAd890419HMNRRV07': u'BAAD890419HMNRRV07',
|
||||||
|
'VIAA900930MMNClL08': u'VIAA900930MMNCLL08',
|
||||||
|
'HEGR891009HMNRRD09': u'HEGR891009HMNRRD09',
|
||||||
|
'MARR890512HMNRMN09': u'MARR890512HMNRMN09',
|
||||||
|
'MESJ890928HMNZNS00': u'MESJ890928HMNZNS00',
|
||||||
|
'BAAA890317HDFRLL03': u'BAAA890317HDFRLL03',
|
||||||
|
'TOMA880125HMNRRNO2': u'TOMA880125HMNRRNO2',
|
||||||
|
'OOMG890727HMNRSR06': u'OOMG890727HMNRSR06',
|
||||||
|
'AAAA000101HDFCCC09': u'AAAA000101HDFCCC09',
|
||||||
|
}
|
||||||
|
invalid = {
|
||||||
|
'AAAA000000HDFCCC09': error_format,
|
||||||
|
'AAAA000000HDFAAA03': error_format,
|
||||||
|
'AAAA000000HXXCCC08': error_format,
|
||||||
|
'AAAA000000XMNCCC02': error_format,
|
||||||
|
'HEGR891009HMNRRD0A': error_format,
|
||||||
|
'MARR890512HMNRMN0A': error_format,
|
||||||
|
'AaMG890608HDFLJL01': error_checksum,
|
||||||
|
'BAAd890419HMNRRV08': error_checksum,
|
||||||
|
'VIAA900930MMNClL09': error_checksum,
|
||||||
|
'MESJ890928HMNZNS01': error_checksum,
|
||||||
|
'BAAA890317HDFRLL04': error_checksum,
|
||||||
|
'TOMA880125HMNRRNO3': error_checksum,
|
||||||
|
'OOMG890727HMNRSR07': error_checksum,
|
||||||
|
}
|
||||||
|
self.assertFieldOutput(MXCURPField, valid, invalid)
|
|
@ -27,6 +27,7 @@ from localflavor.it import ITLocalFlavorTests
|
||||||
from localflavor.jp import JPLocalFlavorTests
|
from localflavor.jp import JPLocalFlavorTests
|
||||||
from localflavor.kw import KWLocalFlavorTests
|
from localflavor.kw import KWLocalFlavorTests
|
||||||
from localflavor.mk import MKLocalFlavorTests
|
from localflavor.mk import MKLocalFlavorTests
|
||||||
|
from localflavor.mx import MXLocalFlavorTests
|
||||||
from localflavor.nl import NLLocalFlavorTests
|
from localflavor.nl import NLLocalFlavorTests
|
||||||
from localflavor.pl import PLLocalFlavorTests
|
from localflavor.pl import PLLocalFlavorTests
|
||||||
from localflavor.pt import PTLocalFlavorTests
|
from localflavor.pt import PTLocalFlavorTests
|
||||||
|
|
|
@ -40,6 +40,7 @@ from regressiontests.forms.localflavortests import (
|
||||||
JPLocalFlavorTests,
|
JPLocalFlavorTests,
|
||||||
KWLocalFlavorTests,
|
KWLocalFlavorTests,
|
||||||
MKLocalFlavorTests,
|
MKLocalFlavorTests,
|
||||||
|
MXLocalFlavorTests,
|
||||||
NLLocalFlavorTests,
|
NLLocalFlavorTests,
|
||||||
PLLocalFlavorTests,
|
PLLocalFlavorTests,
|
||||||
PTLocalFlavorTests,
|
PTLocalFlavorTests,
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
from django.forms import ModelForm
|
||||||
|
from models import MXPersonProfile
|
||||||
|
|
||||||
|
class MXPersonProfileForm(ModelForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MXPersonProfile
|
|
@ -0,0 +1,12 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.localflavor.mx.models import (
|
||||||
|
MXStateField, MXRFCField, MXCURPField, MXZipCodeField)
|
||||||
|
|
||||||
|
class MXPersonProfile(models.Model):
|
||||||
|
state = MXStateField()
|
||||||
|
rfc = MXRFCField()
|
||||||
|
curp = MXCURPField()
|
||||||
|
zip_code = MXZipCodeField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = 'localflavor'
|
|
@ -0,0 +1,71 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from django.test import TestCase
|
||||||
|
from forms import MXPersonProfileForm
|
||||||
|
|
||||||
|
class MXLocalFlavorTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.form = MXPersonProfileForm({
|
||||||
|
'state': 'MIC',
|
||||||
|
'rfc': 'toma880125kv3',
|
||||||
|
'curp': 'toma880125hmnrrn02',
|
||||||
|
'zip_code': '58120',
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_get_display_methods(self):
|
||||||
|
"""Test that the get_*_display() methods are added to the model instances."""
|
||||||
|
place = self.form.save()
|
||||||
|
self.assertEqual(place.get_state_display(), u'Michoacán')
|
||||||
|
|
||||||
|
def test_errors(self):
|
||||||
|
"""Test that required MXFields throw appropriate errors."""
|
||||||
|
form = MXPersonProfileForm({
|
||||||
|
'state': 'Invalid state',
|
||||||
|
'rfc': 'invalid rfc',
|
||||||
|
'curp': 'invalid curp',
|
||||||
|
'zip_code': 'xxx',
|
||||||
|
})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(form.errors['state'], [u'Select a valid choice. Invalid state is not one of the available choices.'])
|
||||||
|
self.assertEqual(form.errors['rfc'], [u'Enter a valid RFC.'])
|
||||||
|
self.assertEqual(form.errors['curp'], [u'Ensure this value has at least 18 characters (it has 12).', u'Enter a valid CURP.'])
|
||||||
|
self.assertEqual(form.errors['zip_code'], [u'Enter a valid zip code in the format XXXXX.'])
|
||||||
|
|
||||||
|
def test_field_blank_option(self):
|
||||||
|
"""Test that the empty option is there."""
|
||||||
|
state_select_html = """\
|
||||||
|
<select name="state" id="id_state">
|
||||||
|
<option value="">---------</option>
|
||||||
|
<option value="AGU">Aguascalientes</option>
|
||||||
|
<option value="BCN">Baja California</option>
|
||||||
|
<option value="BCS">Baja California Sur</option>
|
||||||
|
<option value="CAM">Campeche</option>
|
||||||
|
<option value="CHH">Chihuahua</option>
|
||||||
|
<option value="CHP">Chiapas</option>
|
||||||
|
<option value="COA">Coahuila</option>
|
||||||
|
<option value="COL">Colima</option>
|
||||||
|
<option value="DIF">Distrito Federal</option>
|
||||||
|
<option value="DUR">Durango</option>
|
||||||
|
<option value="GRO">Guerrero</option>
|
||||||
|
<option value="GUA">Guanajuato</option>
|
||||||
|
<option value="HID">Hidalgo</option>
|
||||||
|
<option value="JAL">Jalisco</option>
|
||||||
|
<option value="MEX">Estado de México</option>
|
||||||
|
<option value="MIC" selected="selected">Michoacán</option>
|
||||||
|
<option value="MOR">Morelos</option>
|
||||||
|
<option value="NAY">Nayarit</option>
|
||||||
|
<option value="NLE">Nuevo León</option>
|
||||||
|
<option value="OAX">Oaxaca</option>
|
||||||
|
<option value="PUE">Puebla</option>
|
||||||
|
<option value="QUE">Querétaro</option>
|
||||||
|
<option value="ROO">Quintana Roo</option>
|
||||||
|
<option value="SIN">Sinaloa</option>
|
||||||
|
<option value="SLP">San Luis Potosí</option>
|
||||||
|
<option value="SON">Sonora</option>
|
||||||
|
<option value="TAB">Tabasco</option>
|
||||||
|
<option value="TAM">Tamaulipas</option>
|
||||||
|
<option value="TLA">Tlaxcala</option>
|
||||||
|
<option value="VER">Veracruz</option>
|
||||||
|
<option value="YUC">Yucatán</option>
|
||||||
|
<option value="ZAC">Zacatecas</option>
|
||||||
|
</select>"""
|
||||||
|
self.assertEqual(str(self.form['state']), state_select_html)
|
|
@ -1,4 +1,5 @@
|
||||||
from au.tests import *
|
from au.tests import *
|
||||||
from mk.tests import *
|
from mk.tests import *
|
||||||
|
from mx.tests import *
|
||||||
from us.tests import *
|
from us.tests import *
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,6 @@ from django.forms import ModelForm
|
||||||
from models import USPlace
|
from models import USPlace
|
||||||
|
|
||||||
class USPlaceForm(ModelForm):
|
class USPlaceForm(ModelForm):
|
||||||
"""docstring for PlaceForm"""
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = USPlace
|
model = USPlace
|
||||||
|
|
|
@ -11,5 +11,6 @@ class USPlace(models.Model):
|
||||||
state_default = USStateField(default="CA", blank=True)
|
state_default = USStateField(default="CA", blank=True)
|
||||||
postal_code = USPostalCodeField(blank=True)
|
postal_code = USPostalCodeField(blank=True)
|
||||||
name = models.CharField(max_length=20)
|
name = models.CharField(max_length=20)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'localflavor'
|
app_label = 'localflavor'
|
||||||
|
|
Loading…
Reference in New Issue