From 0fbadfd1c8ce30fd4c9f2857d467047d5bfac63d Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Fri, 29 Jul 2011 10:20:16 +0000 Subject: [PATCH] =?UTF-8?q?Fixed=20#16497=20--=20Added=20new=20form=20and?= =?UTF-8?q?=20model=20fields=20to=20the=20Mexican=20local=20flavor.=20Many?= =?UTF-8?q?=20thanks=20to=20Andr=C3=A9s=20Torres=20Marroqu=C3=ADn=20and=20?= =?UTF-8?q?Gerardo=20Orozco.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: http://code.djangoproject.com/svn/django/trunk@16572 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 2 + django/contrib/localflavor/mx/forms.py | 215 +++++++++++++++++- django/contrib/localflavor/mx/models.py | 70 ++++++ django/contrib/localflavor/mx/mx_states.py | 2 +- docs/ref/contrib/localflavor.txt | 69 ++++++ tests/regressiontests/forms/localflavor/mx.py | 127 +++++++++++ .../regressiontests/forms/localflavortests.py | 1 + tests/regressiontests/forms/tests/__init__.py | 1 + .../regressiontests/localflavor/mk/models.py | 2 +- .../localflavor/mx/__init__.py | 0 tests/regressiontests/localflavor/mx/forms.py | 7 + .../regressiontests/localflavor/mx/models.py | 12 + tests/regressiontests/localflavor/mx/tests.py | 71 ++++++ tests/regressiontests/localflavor/tests.py | 1 + tests/regressiontests/localflavor/us/forms.py | 2 +- .../regressiontests/localflavor/us/models.py | 1 + 16 files changed, 578 insertions(+), 5 deletions(-) create mode 100644 django/contrib/localflavor/mx/models.py create mode 100644 tests/regressiontests/forms/localflavor/mx.py create mode 100644 tests/regressiontests/localflavor/mx/__init__.py create mode 100644 tests/regressiontests/localflavor/mx/forms.py create mode 100644 tests/regressiontests/localflavor/mx/models.py create mode 100644 tests/regressiontests/localflavor/mx/tests.py diff --git a/AUTHORS b/AUTHORS index 31a3300ed7..d751e48dbb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -340,6 +340,7 @@ answer newbie questions, and generally made Django that much better: Nuno Mariz mark@junklight.com Orestis Markou + Andrés Torres Marroquín Takashi Matsuo Zlatko Mašek Yasushi Masuda @@ -380,6 +381,7 @@ answer newbie questions, and generally made Django that much better: Neal Norwitz Todd O'Bryan Selwin Ong + Gerardo Orozco Christian Oudard oggie rob oggy diff --git a/django/contrib/localflavor/mx/forms.py b/django/contrib/localflavor/mx/forms.py index 95819375ef..95ca73d901 100644 --- a/django/contrib/localflavor/mx/forms.py +++ b/django/contrib/localflavor/mx/forms.py @@ -1,14 +1,225 @@ +# -*- coding: utf-8 -*- """ 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): """ A Select widget that uses a list of Mexican states as its choices. """ def __init__(self, attrs=None): - from mx_states import 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 diff --git a/django/contrib/localflavor/mx/models.py b/django/contrib/localflavor/mx/models.py new file mode 100644 index 0000000000..3ef8d5fb03 --- /dev/null +++ b/django/contrib/localflavor/mx/models.py @@ -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) \ No newline at end of file diff --git a/django/contrib/localflavor/mx/mx_states.py b/django/contrib/localflavor/mx/mx_states.py index eed1700efb..2aba63ef26 100644 --- a/django/contrib/localflavor/mx/mx_states.py +++ b/django/contrib/localflavor/mx/mx_states.py @@ -8,6 +8,7 @@ when explicitly needed. from django.utils.translation import ugettext_lazy as _ +# All 31 states, plus the `Distrito Federal`. STATE_CHOICES = ( ('AGU', _(u'Aguascalientes')), ('BCN', _(u'Baja California')), @@ -42,4 +43,3 @@ STATE_CHOICES = ( ('YUC', _(u'Yucatán')), ('ZAC', _(u'Zacatecas')), ) - diff --git a/docs/ref/contrib/localflavor.txt b/docs/ref/contrib/localflavor.txt index 78b997af6b..bd38adc693 100644 --- a/docs/ref/contrib/localflavor.txt +++ b/docs/ref/contrib/localflavor.txt @@ -798,10 +798,79 @@ Macedonia (``mk``) 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 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``) =============== diff --git a/tests/regressiontests/forms/localflavor/mx.py b/tests/regressiontests/forms/localflavor/mx.py new file mode 100644 index 0000000000..1ffc11c834 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/mx.py @@ -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'''''' + 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) diff --git a/tests/regressiontests/forms/localflavortests.py b/tests/regressiontests/forms/localflavortests.py index eb289b5a68..a4db3dba98 100644 --- a/tests/regressiontests/forms/localflavortests.py +++ b/tests/regressiontests/forms/localflavortests.py @@ -27,6 +27,7 @@ from localflavor.it import ITLocalFlavorTests from localflavor.jp import JPLocalFlavorTests from localflavor.kw import KWLocalFlavorTests from localflavor.mk import MKLocalFlavorTests +from localflavor.mx import MXLocalFlavorTests from localflavor.nl import NLLocalFlavorTests from localflavor.pl import PLLocalFlavorTests from localflavor.pt import PTLocalFlavorTests diff --git a/tests/regressiontests/forms/tests/__init__.py b/tests/regressiontests/forms/tests/__init__.py index c45b25b374..f63818d578 100644 --- a/tests/regressiontests/forms/tests/__init__.py +++ b/tests/regressiontests/forms/tests/__init__.py @@ -40,6 +40,7 @@ from regressiontests.forms.localflavortests import ( JPLocalFlavorTests, KWLocalFlavorTests, MKLocalFlavorTests, + MXLocalFlavorTests, NLLocalFlavorTests, PLLocalFlavorTests, PTLocalFlavorTests, diff --git a/tests/regressiontests/localflavor/mk/models.py b/tests/regressiontests/localflavor/mk/models.py index b79239a9bf..244f396b52 100644 --- a/tests/regressiontests/localflavor/mk/models.py +++ b/tests/regressiontests/localflavor/mk/models.py @@ -8,7 +8,7 @@ class MKPerson(models.Model): umcn = UMCNField() id_number = MKIdentityCardNumberField() municipality = MKMunicipalityField(blank = True) - municipality_req = MKMunicipalityField(blank = False) + municipality_req = MKMunicipalityField(blank = False) class Meta: app_label = 'localflavor' diff --git a/tests/regressiontests/localflavor/mx/__init__.py b/tests/regressiontests/localflavor/mx/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regressiontests/localflavor/mx/forms.py b/tests/regressiontests/localflavor/mx/forms.py new file mode 100644 index 0000000000..2669cd9ace --- /dev/null +++ b/tests/regressiontests/localflavor/mx/forms.py @@ -0,0 +1,7 @@ +from django.forms import ModelForm +from models import MXPersonProfile + +class MXPersonProfileForm(ModelForm): + + class Meta: + model = MXPersonProfile diff --git a/tests/regressiontests/localflavor/mx/models.py b/tests/regressiontests/localflavor/mx/models.py new file mode 100644 index 0000000000..9c32cf5cab --- /dev/null +++ b/tests/regressiontests/localflavor/mx/models.py @@ -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' diff --git a/tests/regressiontests/localflavor/mx/tests.py b/tests/regressiontests/localflavor/mx/tests.py new file mode 100644 index 0000000000..40da38a97c --- /dev/null +++ b/tests/regressiontests/localflavor/mx/tests.py @@ -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 = """\ +""" + self.assertEqual(str(self.form['state']), state_select_html) diff --git a/tests/regressiontests/localflavor/tests.py b/tests/regressiontests/localflavor/tests.py index 6a15d463f7..95a5f99128 100644 --- a/tests/regressiontests/localflavor/tests.py +++ b/tests/regressiontests/localflavor/tests.py @@ -1,4 +1,5 @@ from au.tests import * from mk.tests import * +from mx.tests import * from us.tests import * diff --git a/tests/regressiontests/localflavor/us/forms.py b/tests/regressiontests/localflavor/us/forms.py index 9b77e1078b..7730662f8c 100644 --- a/tests/regressiontests/localflavor/us/forms.py +++ b/tests/regressiontests/localflavor/us/forms.py @@ -2,6 +2,6 @@ from django.forms import ModelForm from models import USPlace class USPlaceForm(ModelForm): - """docstring for PlaceForm""" + class Meta: model = USPlace diff --git a/tests/regressiontests/localflavor/us/models.py b/tests/regressiontests/localflavor/us/models.py index 7ac3826b2f..4095f73532 100644 --- a/tests/regressiontests/localflavor/us/models.py +++ b/tests/regressiontests/localflavor/us/models.py @@ -11,5 +11,6 @@ class USPlace(models.Model): state_default = USStateField(default="CA", blank=True) postal_code = USPostalCodeField(blank=True) name = models.CharField(max_length=20) + class Meta: app_label = 'localflavor'