import copy from datetime import datetime from django.forms import ( CharField, FileInput, Form, MultipleChoiceField, MultiValueField, MultiWidget, RadioSelect, SelectMultiple, SplitDateTimeField, SplitDateTimeWidget, TextInput, ) from .base import WidgetTest class MyMultiWidget(MultiWidget): def decompress(self, value): if value: return value.split("__") return ["", ""] class ComplexMultiWidget(MultiWidget): def __init__(self, attrs=None): widgets = ( TextInput(), SelectMultiple(choices=WidgetTest.beatles), SplitDateTimeWidget(), ) super().__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] class ComplexField(MultiValueField): def __init__(self, required=True, widget=None, label=None, initial=None): fields = ( CharField(), MultipleChoiceField(choices=WidgetTest.beatles), SplitDateTimeField(), ) super().__init__( fields, required=required, widget=widget, label=label, initial=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 DeepCopyWidget(MultiWidget): """ Used to test MultiWidget.__deepcopy__(). """ def __init__(self, choices=[]): widgets = [ RadioSelect(choices=choices), TextInput, ] super().__init__(widgets) def _set_choices(self, choices): """ When choices are set for this widget, we want to pass those along to the Select widget. """ self.widgets[0].choices = choices def _get_choices(self): """ The choices for this widget are the Select widget's choices. """ return self.widgets[0].choices choices = property(_get_choices, _set_choices) class MultiWidgetTest(WidgetTest): def test_subwidgets_name(self): widget = MultiWidget( widgets={ "": TextInput(), "big": TextInput(attrs={"class": "big"}), "small": TextInput(attrs={"class": "small"}), }, ) self.check_html( widget, "name", ["John", "George", "Paul"], html=( '' '' '' ), ) def test_text_inputs(self): widget = MyMultiWidget( widgets=( TextInput(attrs={"class": "big"}), TextInput(attrs={"class": "small"}), ) ) self.check_html( widget, "name", ["john", "lennon"], html=( '' '' ), ) self.check_html( widget, "name", ("john", "lennon"), html=( '' '' ), ) self.check_html( widget, "name", "john__lennon", html=( '' '' ), ) self.check_html( widget, "name", "john__lennon", attrs={"id": "foo"}, html=( '' '' ), ) def test_constructor_attrs(self): widget = MyMultiWidget( widgets=( TextInput(attrs={"class": "big"}), TextInput(attrs={"class": "small"}), ), attrs={"id": "bar"}, ) self.check_html( widget, "name", ["john", "lennon"], html=( '' '' ), ) def test_constructor_attrs_with_type(self): attrs = {"type": "number"} widget = MyMultiWidget(widgets=(TextInput, TextInput()), attrs=attrs) self.check_html( widget, "code", ["1", "2"], html=( '' '' ), ) widget = MyMultiWidget( widgets=(TextInput(attrs), TextInput(attrs)), attrs={"class": "bar"} ) self.check_html( widget, "code", ["1", "2"], html=( '' '' ), ) def test_value_omitted_from_data(self): widget = MyMultiWidget(widgets=(TextInput(), TextInput())) self.assertIs(widget.value_omitted_from_data({}, {}, "field"), True) self.assertIs( widget.value_omitted_from_data({"field_0": "x"}, {}, "field"), False ) self.assertIs( widget.value_omitted_from_data({"field_1": "y"}, {}, "field"), False ) self.assertIs( widget.value_omitted_from_data( {"field_0": "x", "field_1": "y"}, {}, "field" ), False, ) def test_value_from_datadict_subwidgets_name(self): widget = MultiWidget(widgets={"x": TextInput(), "": TextInput()}) tests = [ ({}, [None, None]), ({"field": "x"}, [None, "x"]), ({"field_x": "y"}, ["y", None]), ({"field": "x", "field_x": "y"}, ["y", "x"]), ] for data, expected in tests: with self.subTest(data): self.assertEqual( widget.value_from_datadict(data, {}, "field"), expected, ) def test_value_omitted_from_data_subwidgets_name(self): widget = MultiWidget(widgets={"x": TextInput(), "": TextInput()}) tests = [ ({}, True), ({"field": "x"}, False), ({"field_x": "y"}, False), ({"field": "x", "field_x": "y"}, False), ] for data, expected in tests: with self.subTest(data): self.assertIs( widget.value_omitted_from_data(data, {}, "field"), expected, ) def test_needs_multipart_true(self): """ needs_multipart_form should be True if any widgets need it. """ widget = MyMultiWidget(widgets=(TextInput(), FileInput())) self.assertTrue(widget.needs_multipart_form) def test_needs_multipart_false(self): """ needs_multipart_form should be False if no widgets need it. """ widget = MyMultiWidget(widgets=(TextInput(), TextInput())) self.assertFalse(widget.needs_multipart_form) def test_nested_multiwidget(self): """ MultiWidgets can be composed of other MultiWidgets. """ widget = ComplexMultiWidget() self.check_html( widget, "name", "some text,JP,2007-04-25 06:24:00", html=( """ """ ), ) def test_no_whitespace_between_widgets(self): widget = MyMultiWidget(widgets=(TextInput, TextInput())) self.check_html( widget, "code", None, html=(''), strict=True, ) def test_deepcopy(self): """ MultiWidget should define __deepcopy__() (#12048). """ w1 = DeepCopyWidget(choices=[1, 2, 3]) w2 = copy.deepcopy(w1) w2.choices = [4, 5, 6] # w2 ought to be independent of w1, since MultiWidget ought # to make a copy of its sub-widgets when it is copied. self.assertEqual(w1.choices, [1, 2, 3]) def test_fieldset(self): class TestForm(Form): template_name = "forms_tests/use_fieldset.html" field = ComplexField(widget=ComplexMultiWidget) form = TestForm() self.assertIs(form["field"].field.widget.use_fieldset, True) self.assertHTMLEqual( "
", form.render(), )