Skip to content

Commit

Permalink
Enable customization of SimpleSetField
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Chainz committed Jun 1, 2015
1 parent 17efca3 commit 11e416e
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 11 deletions.
47 changes: 36 additions & 11 deletions django_mysql/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

class SimpleListField(forms.CharField):

# These bits can be overridden simply to change the way the field works
# These bits can be overridden to change the way the field serializes and
# deserializes for the user, e.g. line-delimited, json, etc.

default_error_messages = {
'item_n_invalid': _('Item %(nth)s in the list did not validate: '),
Expand Down Expand Up @@ -138,16 +139,36 @@ def run_validators(self, value):


class SimpleSetField(forms.CharField):
empty_values = list(validators.EMPTY_VALUES) + [set()]

# These bits can be overridden to change the way the field serializes and
# deserializes for the user, e.g. line-delimited, json, etc.

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: '),
'items_no_commas': _('No leading, trailing, or double commas.'),
'no_duplicates': _("Duplicates are not supported. "
"'%(item)s' appears twice or more.")
"'%(item)s' appears twice or more."),
'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

empty_values = list(validators.EMPTY_VALUES) + [set()]

def __init__(self, base_field, max_length=None, min_length=None,
*args, **kwargs):
self.base_field = base_field
Expand All @@ -161,22 +182,26 @@ def __init__(self, base_field, max_length=None, min_length=None,

def prepare_value(self, value):
if isinstance(value, set):
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 = set()
for i, item in enumerate(items, start=1):
if not len(item):
errors.append(ValidationError(
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',
Expand Down
63 changes: 63 additions & 0 deletions tests/django_mysql_tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,66 @@ def test_required(self):
with pytest.raises(exceptions.ValidationError) as excinfo:
field.clean('')
assert excinfo.value.messages[0] == 'This field is required.'


class CustomSetField(SimpleSetField):

default_error_messages = dict(
SimpleSetField.default_error_messages,
items_no_commas="Commas are not allowed.",
items_no_empty="Empty items are not allowed."
)

def prepare_value_serialize(self, values):
return "&".join(values)

def to_python_deserialize(self, value):
if not value:
return []
else:
return [v for v in value.split('&')]


class CustomSetFieldTests(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 = CustomSetField(forms.CharField())
value = field.clean('a&b&c')
assert value == {'a', 'b', 'c'}

def test_to_python_no_empties(self):
field = CustomSetField(forms.IntegerField())
with pytest.raises(exceptions.ValidationError) as excinfo:
field.clean('1&')
assert (
excinfo.value.messages[0] ==
'Empty items are not allowed.'
)

def test_to_python_no_commas(self):
field = CustomSetField(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 = CustomSetField(forms.IntegerField())
with pytest.raises(exceptions.ValidationError) as excinfo:
field.clean('a&b&9')
assert (
excinfo.value.messages[0] ==
'Item 1 in the set did not validate: Enter a whole number.'
)

def test_prepare_value(self):
field = CustomSetField(forms.CharField())
value = field.prepare_value({'a', 'b', 'c'})
assert set(value.split('&')) == {'a', 'b', 'c'}

assert field.prepare_value('1&a') == '1&a'

0 comments on commit 11e416e

Please sign in to comment.