2015-08-31 10:13:42 +08:00
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
from django.forms import (
|
|
|
|
CharField, Form, MultipleChoiceField, MultiValueField, MultiWidget,
|
|
|
|
SelectMultiple, SplitDateTimeField, SplitDateTimeWidget, TextInput,
|
|
|
|
ValidationError,
|
|
|
|
)
|
|
|
|
from django.test import SimpleTestCase
|
|
|
|
|
|
|
|
beatles = (('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))
|
|
|
|
|
|
|
|
|
|
|
|
class ComplexMultiWidget(MultiWidget):
|
|
|
|
def __init__(self, attrs=None):
|
|
|
|
widgets = (
|
|
|
|
TextInput(),
|
|
|
|
SelectMultiple(choices=beatles),
|
|
|
|
SplitDateTimeWidget(),
|
|
|
|
)
|
|
|
|
super(ComplexMultiWidget, self).__init__(widgets, attrs)
|
|
|
|
|
|
|
|
def decompress(self, value):
|
|
|
|
if value:
|
|
|
|
data = value.split(',')
|
|
|
|
return [
|
|
|
|
data[0],
|
|
|
|
list(data[1]),
|
|
|
|
datetime.strptime(data[2], "%Y-%m-%d %H:%M:%S"),
|
|
|
|
]
|
|
|
|
return [None, None, None]
|
|
|
|
|
|
|
|
def format_output(self, rendered_widgets):
|
|
|
|
return '\n'.join(rendered_widgets)
|
|
|
|
|
|
|
|
|
|
|
|
class ComplexField(MultiValueField):
|
|
|
|
def __init__(self, required=True, widget=None, label=None, initial=None):
|
|
|
|
fields = (
|
|
|
|
CharField(),
|
|
|
|
MultipleChoiceField(choices=beatles),
|
|
|
|
SplitDateTimeField(),
|
|
|
|
)
|
|
|
|
super(ComplexField, self).__init__(fields, required, widget, label, initial)
|
|
|
|
|
|
|
|
def compress(self, data_list):
|
|
|
|
if data_list:
|
|
|
|
return '%s,%s,%s' % (data_list[0], ''.join(data_list[1]), data_list[2])
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
class ComplexFieldForm(Form):
|
|
|
|
field1 = ComplexField(widget=ComplexMultiWidget())
|
|
|
|
|
|
|
|
|
|
|
|
class MultiValueFieldTest(SimpleTestCase):
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
|
|
|
cls.field = ComplexField(widget=ComplexMultiWidget())
|
|
|
|
super(MultiValueFieldTest, cls).setUpClass()
|
|
|
|
|
|
|
|
def test_clean(self):
|
|
|
|
self.assertEqual(
|
|
|
|
self.field.clean(['some text', ['J', 'P'], ['2007-04-25', '6:24:00']]),
|
|
|
|
'some text,JP,2007-04-25 06:24:00',
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_bad_choice(self):
|
|
|
|
msg = "'Select a valid choice. X is not one of the available choices.'"
|
|
|
|
with self.assertRaisesMessage(ValidationError, msg):
|
|
|
|
self.field.clean(['some text', ['X'], ['2007-04-25', '6:24:00']])
|
|
|
|
|
|
|
|
def test_no_value(self):
|
|
|
|
"""
|
|
|
|
If insufficient data is provided, None is substituted.
|
|
|
|
"""
|
|
|
|
msg = "'This field is required.'"
|
|
|
|
with self.assertRaisesMessage(ValidationError, msg):
|
|
|
|
self.field.clean(['some text', ['JP']])
|
|
|
|
|
|
|
|
def test_has_changed_no_initial(self):
|
2016-04-10 01:17:34 +08:00
|
|
|
self.assertTrue(self.field.has_changed(None, ['some text', ['J', 'P'], ['2007-04-25', '6:24:00']]))
|
2015-08-31 10:13:42 +08:00
|
|
|
|
|
|
|
def test_has_changed_same(self):
|
|
|
|
self.assertFalse(self.field.has_changed(
|
|
|
|
'some text,JP,2007-04-25 06:24:00',
|
|
|
|
['some text', ['J', 'P'], ['2007-04-25', '6:24:00']],
|
|
|
|
))
|
|
|
|
|
|
|
|
def test_has_changed_first_widget(self):
|
|
|
|
"""
|
|
|
|
Test when the first widget's data has changed.
|
|
|
|
"""
|
|
|
|
self.assertTrue(self.field.has_changed(
|
|
|
|
'some text,JP,2007-04-25 06:24:00',
|
|
|
|
['other text', ['J', 'P'], ['2007-04-25', '6:24:00']],
|
|
|
|
))
|
|
|
|
|
|
|
|
def test_has_changed_last_widget(self):
|
|
|
|
"""
|
|
|
|
Test when the last widget's data has changed. This ensures that it is
|
|
|
|
not short circuiting while testing the widgets.
|
|
|
|
"""
|
|
|
|
self.assertTrue(self.field.has_changed(
|
|
|
|
'some text,JP,2007-04-25 06:24:00',
|
|
|
|
['some text', ['J', 'P'], ['2009-04-25', '11:44:00']],
|
|
|
|
))
|
|
|
|
|
|
|
|
def test_form_as_table(self):
|
|
|
|
form = ComplexFieldForm()
|
|
|
|
self.assertHTMLEqual(
|
|
|
|
form.as_table(),
|
|
|
|
"""
|
|
|
|
<tr><th><label for="id_field1_0">Field1:</label></th>
|
2016-03-29 02:02:04 +08:00
|
|
|
<td><input type="text" name="field1_0" id="id_field1_0" required />
|
|
|
|
<select multiple="multiple" name="field1_1" id="id_field1_1" required>
|
2015-08-31 10:13:42 +08:00
|
|
|
<option value="J">John</option>
|
|
|
|
<option value="P">Paul</option>
|
|
|
|
<option value="G">George</option>
|
|
|
|
<option value="R">Ringo</option>
|
|
|
|
</select>
|
2016-03-29 02:02:04 +08:00
|
|
|
<input type="text" name="field1_2_0" id="id_field1_2_0" required />
|
|
|
|
<input type="text" name="field1_2_1" id="id_field1_2_1" required /></td></tr>
|
2015-08-31 10:13:42 +08:00
|
|
|
""",
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_form_as_table_data(self):
|
|
|
|
form = ComplexFieldForm({
|
|
|
|
'field1_0': 'some text',
|
|
|
|
'field1_1': ['J', 'P'],
|
|
|
|
'field1_2_0': '2007-04-25',
|
|
|
|
'field1_2_1': '06:24:00',
|
|
|
|
})
|
|
|
|
self.assertHTMLEqual(
|
|
|
|
form.as_table(),
|
|
|
|
"""
|
|
|
|
<tr><th><label for="id_field1_0">Field1:</label></th>
|
2016-03-29 02:02:04 +08:00
|
|
|
<td><input type="text" name="field1_0" value="some text" id="id_field1_0" required />
|
|
|
|
<select multiple="multiple" name="field1_1" id="id_field1_1" required>
|
2015-08-31 10:13:42 +08:00
|
|
|
<option value="J" selected="selected">John</option>
|
|
|
|
<option value="P" selected="selected">Paul</option>
|
|
|
|
<option value="G">George</option>
|
|
|
|
<option value="R">Ringo</option>
|
|
|
|
</select>
|
2016-03-29 02:02:04 +08:00
|
|
|
<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" required />
|
|
|
|
<input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" required /></td></tr>
|
2015-08-31 10:13:42 +08:00
|
|
|
""",
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_form_cleaned_data(self):
|
|
|
|
form = ComplexFieldForm({
|
|
|
|
'field1_0': 'some text',
|
|
|
|
'field1_1': ['J', 'P'],
|
|
|
|
'field1_2_0': '2007-04-25',
|
|
|
|
'field1_2_1': '06:24:00',
|
|
|
|
})
|
|
|
|
form.is_valid()
|
2016-04-10 01:17:34 +08:00
|
|
|
self.assertEqual(form.cleaned_data['field1'], 'some text,JP,2007-04-25 06:24:00')
|