summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2016-09-30 14:49:50 -0400
committerGitHub <noreply@github.com>2016-09-30 14:49:50 -0400
commit87c5e7efebd040aef0f0479ccf86877155bb5cea (patch)
tree7306b48f0da0844ca0f0f2e4347d9453eb11012e
parent1d25eb9688f1a245737490dccf8ba54b6c907052 (diff)
Refs #27186 -- Fixed model form default fallback for CheckboxSelectMultiple.
-rw-r--r--django/forms/widgets.py5
-rw-r--r--docs/ref/forms/widgets.txt9
-rw-r--r--docs/releases/1.10.2.txt8
-rw-r--r--docs/topics/forms/modelforms.txt3
-rw-r--r--tests/forms_tests/widget_tests/test_checkboxselectmultiple.py5
-rw-r--r--tests/model_forms/tests.py18
6 files changed, 38 insertions, 10 deletions
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index f9cfaee9f9..427ea469e2 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -817,6 +817,11 @@ class CheckboxSelectMultiple(RendererMixin, SelectMultiple):
# require all checkboxes to be checked instead of at least one.
return False
+ def value_omitted_from_data(self, data, files, name):
+ # HTML checkboxes don't appear in POST data if not checked, so it's
+ # never known if the value is actually omitted.
+ return False
+
class MultiWidget(Widget):
"""
diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
index 8a6864b5b7..99514e46a2 100644
--- a/docs/ref/forms/widgets.txt
+++ b/docs/ref/forms/widgets.txt
@@ -280,10 +280,11 @@ foundation for custom widgets.
The method's result affects whether or not a field in a model form
:ref:`falls back to its default <topics-modelform-save>`.
- A special case is :class:`~django.forms.CheckboxInput`, which always
- returns ``False`` because an unchecked checkbox doesn't appear in the
- data of an HTML form submission, so it's unknown whether or not the
- user actually submitted a value.
+ Special cases are :class:`~django.forms.CheckboxInput` and
+ :class:`~django.forms.CheckboxSelectMultiple`, which always return
+ ``False`` because an unchecked checkbox doesn't appear in the data of
+ an HTML form submission, so it's unknown whether or not the user
+ actually submitted a value.
``MultiWidget``
---------------
diff --git a/docs/releases/1.10.2.txt b/docs/releases/1.10.2.txt
index 075eb947f9..78447dccba 100644
--- a/docs/releases/1.10.2.txt
+++ b/docs/releases/1.10.2.txt
@@ -18,10 +18,10 @@ Bugfixes
* Disabled system check for URL patterns beginning with a '/' when
``APPEND_SLASH=False`` (:ticket:`27238`).
-* Fixed model form ``default`` fallback for ``MultiWidget``, ``FileInput``,
- ``SplitDateTimeWidget``, ``SelectDateWidget``, and ``SplitArrayWidget``
- (:ticket:`27186`). Custom widgets affected by this issue may need to
- implement a :meth:`~django.forms.Widget.value_omitted_from_data` method.
+* Fixed model form ``default`` fallback for ``CheckboxSelectMultiple``,
+ ``MultiWidget``, ``FileInput``, ``SplitDateTimeWidget``, ``SelectDateWidget``,
+ and ``SplitArrayWidget`` (:ticket:`27186`). Custom widgets affected by this
+ issue should implement :meth:`~django.forms.Widget.value_omitted_from_data`.
* Fixed a crash in ``runserver`` logging during a "Broken pipe" error
(:ticket:`27271`).
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index e0b957f9b3..bc0c124014 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -337,7 +337,8 @@ doesn't validate -- i.e., if ``form.errors`` evaluates to ``True``.
If an optional field doesn't appear in the form's data, the resulting model
instance uses the model field :attr:`~django.db.models.Field.default`, if
there is one, for that field. This behavior doesn't apply to fields that use
-:class:`~django.forms.CheckboxInput` (or any custom widget whose
+:class:`~django.forms.CheckboxInput` and
+:class:`~django.forms.CheckboxSelectMultiple` (or any custom widget whose
:meth:`~django.forms.Widget.value_omitted_from_data` method always returns
``False``) since an unchecked checkbox doesn't appear in the data of an HTML
form submission. Use a custom form field or widget if you're designing an API
diff --git a/tests/forms_tests/widget_tests/test_checkboxselectmultiple.py b/tests/forms_tests/widget_tests/test_checkboxselectmultiple.py
index 2d73f2b589..27e870559d 100644
--- a/tests/forms_tests/widget_tests/test_checkboxselectmultiple.py
+++ b/tests/forms_tests/widget_tests/test_checkboxselectmultiple.py
@@ -120,3 +120,8 @@ class CheckboxSelectMultipleTest(WidgetTest):
self.assertIs(widget.use_required_attribute(None), False)
self.assertIs(widget.use_required_attribute([]), False)
self.assertIs(widget.use_required_attribute(['J', 'P']), False)
+
+ def test_value_omitted_from_data(self):
+ widget = self.widget(choices=self.beatles)
+ self.assertIs(widget.value_omitted_from_data({}, {}, 'field'), False)
+ self.assertIs(widget.value_omitted_from_data({'field': 'value'}, {}, 'field'), False)
diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py
index de7c36271f..53e70c6ae9 100644
--- a/tests/model_forms/tests.py
+++ b/tests/model_forms/tests.py
@@ -601,6 +601,22 @@ class ModelFormBaseTest(TestCase):
self.assertIsInstance(mf1.fields['active'].widget, forms.CheckboxInput)
self.assertIs(m1._meta.get_field('active').get_default(), True)
+ def test_default_not_populated_on_checkboxselectmultiple(self):
+ class PubForm(forms.ModelForm):
+ mode = forms.CharField(required=False, widget=forms.CheckboxSelectMultiple)
+
+ class Meta:
+ model = PublicationDefaults
+ fields = ('mode',)
+
+ # Empty data doesn't use the model default because an unchecked
+ # CheckboxSelectMultiple doesn't have a value in HTML form submission.
+ mf1 = PubForm({})
+ self.assertEqual(mf1.errors, {})
+ m1 = mf1.save(commit=False)
+ self.assertEqual(m1.mode, '')
+ self.assertEqual(m1._meta.get_field('mode').get_default(), 'di')
+
def test_prefixed_form_with_default_field(self):
class PubForm(forms.ModelForm):
prefix = 'form-prefix'
@@ -651,7 +667,7 @@ class ModelFormBaseTest(TestCase):
m2 = mf2.save(commit=False)
self.assertEqual(m2.file.name, 'name')
- def test_selectdatewidget(self):
+ def test_default_selectdatewidget(self):
class PubForm(forms.ModelForm):
date_published = forms.DateField(required=False, widget=forms.SelectDateWidget)