From 17efca3aa2be99834b06857340cb3faaa61d677d Mon Sep 17 00:00:00 2001 From: Adam Chainz Date: Mon, 1 Jun 2015 18:33:26 +0100 Subject: [PATCH] Enable customization of SimpleListField --- django_mysql/forms.py | 48 ++++++++++++++------ tests/django_mysql_tests/test_forms.py | 63 ++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 13 deletions(-) diff --git a/django_mysql/forms.py b/django_mysql/forms.py index a78b03fa..6e5b1c1a 100644 --- a/django_mysql/forms.py +++ b/django_mysql/forms.py @@ -18,11 +18,29 @@ class SimpleListField(forms.CharField): + # These bits can be overridden simply to change the way the field works + default_error_messages = { 'item_n_invalid': _('Item %(nth)s in the list did not validate: '), - 'no_double_commas': _('No leading, trailing, or double commas.'), + 'items_no_commas': _('No leading, trailing, or double commas.'), + # The 'empty' message is the same as 'no commas' by default, since the + # only reason empty strings could arise with the basic comma-splitting + # logic is with extra commas. This may not be true in custom subclasses + # however. + 'items_no_empty': _('No leading, trailing, or double commas.'), } + def prepare_value_serialize(self, values): + return ",".join(values) + + def to_python_deserialize(self, value): + if not value: + return [] + else: + return value.split(",") + + # Internals + def __init__(self, base_field, max_length=None, min_length=None, *args, **kwargs): self.base_field = base_field @@ -36,25 +54,29 @@ def __init__(self, base_field, max_length=None, min_length=None, def prepare_value(self, value): if isinstance(value, list): - return ",".join( - six.text_type(self.base_field.prepare_value(v)) - for v in value + return self.prepare_value_serialize( + (six.text_type(self.base_field.prepare_value(v)) + for v in value) ) return value def to_python(self, value): - if value and len(value): - items = value.split(",") - else: - items = [] + items = self.to_python_deserialize(value) errors = [] values = [] for i, item in enumerate(items, start=1): if not len(item): errors.append(ValidationError( - self.error_messages['no_double_commas'], - code='no_double_commas', + self.error_messages['items_no_empty'], + code='items_no_empty', + )) + continue + + if ',' in item: + errors.append(ValidationError( + self.error_messages['items_no_commas'], + code='items_no_commas', )) continue @@ -121,7 +143,7 @@ class SimpleSetField(forms.CharField): default_error_messages = { 'item_invalid': _('Item "%(item)s" in the set did not validate: '), 'item_n_invalid': _('Item %(nth)s in the set did not validate: '), - 'no_double_commas': _('No leading, trailing, or double commas.'), + 'items_no_commas': _('No leading, trailing, or double commas.'), 'no_duplicates': _("Duplicates are not supported. " "'%(item)s' appears twice or more.") } @@ -156,8 +178,8 @@ def to_python(self, value): for i, item in enumerate(items, start=1): if not len(item): errors.append(ValidationError( - self.error_messages['no_double_commas'], - code='no_double_commas', + self.error_messages['items_no_commas'], + code='items_no_commas', )) continue diff --git a/tests/django_mysql_tests/test_forms.py b/tests/django_mysql_tests/test_forms.py index 21a344ca..f523204d 100644 --- a/tests/django_mysql_tests/test_forms.py +++ b/tests/django_mysql_tests/test_forms.py @@ -130,6 +130,69 @@ def test_required(self): assert excinfo.value.messages[0] == 'This field is required.' +class CustomListField(SimpleListField): + + default_error_messages = dict( + SimpleListField.default_error_messages, + items_no_commas="Commas are not allowed.", + items_no_empty="Empty lines are not allowed." + ) + + def prepare_value_serialize(self, values): + return "\n".join(values) + + def to_python_deserialize(self, value): + if not value: + return [] + else: + return value.splitlines() + + +class CustomListFieldTests(TestCase): + """ + Check that we can subclass SimpleListField and replace how data is + converted back and forth from the form easily + """ + def test_valid(self): + field = CustomListField(forms.CharField()) + value = field.clean('a\nb\nc') + assert value == ['a', 'b', 'c'] + + def test_to_python_no_empties(self): + field = CustomListField(forms.IntegerField()) + with pytest.raises(exceptions.ValidationError) as excinfo: + field.clean('\n\n') + assert ( + excinfo.value.messages[0] == + 'Empty lines are not allowed.' + ) + + def test_to_python_no_commas(self): + field = CustomListField(forms.IntegerField()) + with pytest.raises(exceptions.ValidationError) as excinfo: + field.clean(',1') + assert ( + excinfo.value.messages[0] == + 'Commas are not allowed.' + ) + + def test_to_python_base_field_does_not_validate(self): + field = CustomListField(forms.IntegerField()) + with pytest.raises(exceptions.ValidationError) as excinfo: + field.clean('a\nb\n9') + assert ( + excinfo.value.messages[0] == + 'Item 1 in the list did not validate: Enter a whole number.' + ) + + def test_prepare_value(self): + field = CustomListField(forms.CharField()) + value = field.prepare_value(['a', 'b', 'c']) + assert value.split('\n') == ['a', 'b', 'c'] + + assert field.prepare_value('1\na') == '1\na' + + class TestSimpleSetField(TestCase): def test_valid(self):