diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 49b148e..be7b895 100644
a
|
b
|
__all__ = (
|
24 | 24 | 'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput', |
25 | 25 | 'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', |
26 | 26 | 'CheckboxSelectMultiple', 'MultiWidget', |
27 | | 'SplitDateTimeWidget', |
| 27 | 'SplitDateTimeWidget', 'NullableWidget', |
28 | 28 | ) |
29 | 29 | |
30 | 30 | MEDIA_TYPES = ('css','js') |
… |
… |
class SplitHiddenDateTimeWidget(SplitDateTimeWidget):
|
900 | 900 | for widget in self.widgets: |
901 | 901 | widget.input_type = 'hidden' |
902 | 902 | widget.is_hidden = True |
| 903 | |
| 904 | class NullableWidget(MultiWidget): |
| 905 | """ |
| 906 | A Widget that wraps another Widget and renders it along with a checkbox |
| 907 | that unchecked means that the value should be None (NULL) [#4136] |
| 908 | """ |
| 909 | def __init__(self, subwidget, attrs=None): |
| 910 | self.checkbox = CheckboxInput() |
| 911 | self.subwidget = subwidget |
| 912 | super(NullableWidget, self).__init__( |
| 913 | [self.checkbox, self.subwidget], attrs |
| 914 | ) |
| 915 | |
| 916 | def decompress(self, value): |
| 917 | if value is None: |
| 918 | return [False, ''] |
| 919 | else: |
| 920 | return [True, value] |
| 921 | |
| 922 | def value_from_datadict(self, data, files, name): |
| 923 | is_set, value = super(NullableWidget, self).value_from_datadict( |
| 924 | data, files, name |
| 925 | ) |
| 926 | if is_set: |
| 927 | return value |
| 928 | else: |
| 929 | return None |
diff --git a/tests/regressiontests/forms/tests/widgets.py b/tests/regressiontests/forms/tests/widgets.py
index ce15b8b..54991b3 100644
a
|
b
|
class ClearableFileInputTests(TestCase):
|
1189 | 1189 | data={'myfile-clear': True}, |
1190 | 1190 | files={'myfile': f}, |
1191 | 1191 | name='myfile'), f) |
| 1192 | |
| 1193 | def test_nullable_rendering(self): |
| 1194 | """ |
| 1195 | Test if NullableWidget is rendered correctly. |
| 1196 | """ |
| 1197 | w= NullableWidget(TextInput()) |
| 1198 | self.assertHTMLEqual( |
| 1199 | w.render('email', ''), |
| 1200 | u'<input type="checkbox" checked="checked" name="email_0" />' |
| 1201 | u'<input type="text" name="email_1" />' |
| 1202 | ) |
| 1203 | self.assertHTMLEqual( |
| 1204 | w.render('email', 'test@example.com'), |
| 1205 | u'<input type="checkbox" checked="checked" name="email_0" />' |
| 1206 | u'<input type="text" name="email_1" value="test@example.com" />' |
| 1207 | ) |
| 1208 | self.assertHTMLEqual( |
| 1209 | w.render('email', None), |
| 1210 | u'<input type="checkbox" name="email_0" />' |
| 1211 | u'<input type="text" name="email_1" />' |
| 1212 | ) |
| 1213 | |
| 1214 | def test_nullable_rendering(self): |
| 1215 | """ |
| 1216 | Test if NullableWidget is rendered correctly. |
| 1217 | """ |
| 1218 | w= NullableWidget(TextInput()) |
| 1219 | self.assertEqual(w.value_from_datadict( |
| 1220 | data={'email_0': True, 'email_1': 'test@example.net'}, |
| 1221 | files={}, |
| 1222 | name='email' |
| 1223 | ), 'test@example.net') |
| 1224 | |
| 1225 | self.assertEqual(w.value_from_datadict( |
| 1226 | data={'email_0': False, 'email_1': 'test@example.net'}, |
| 1227 | files={}, |
| 1228 | name='email' |
| 1229 | ), None) |