summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorheathervm <heathervmurray@gmail.com>2017-03-31 07:10:08 -0700
committerTim Graham <timograham@gmail.com>2017-03-31 10:10:08 -0400
commit7d1e23775344cc3dead03bd4af45f4fdf134b819 (patch)
tree0b1b7aa765e7d21bf9d90f34be87563e32266f60
parenta0d29a9abe93cbe87cf818c3b538506e49739b6c (diff)
Fixed #27993 -- Fixed model form default fallback for SelectMultiple.
-rw-r--r--django/forms/widgets.py5
-rw-r--r--docs/ref/forms/widgets.txt11
-rw-r--r--docs/releases/1.10.7.txt3
-rw-r--r--docs/topics/forms/modelforms.txt12
-rw-r--r--tests/forms_tests/widget_tests/test_selectmultiple.py5
-rw-r--r--tests/model_forms/tests.py16
6 files changed, 42 insertions, 10 deletions
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 36485e5011..2e58d449c4 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -714,6 +714,11 @@ class SelectMultiple(Select):
getter = data.get
return getter(name)
+ def value_omitted_from_data(self, data, files, name):
+ # An unselected <select multiple> doesn't appear in POST data, so it's
+ # never known if the value is actually omitted.
+ return False
+
class RadioSelect(ChoiceWidget):
input_type = 'radio'
diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
index 9cc474aae6..828033352a 100644
--- a/docs/ref/forms/widgets.txt
+++ b/docs/ref/forms/widgets.txt
@@ -297,11 +297,12 @@ 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>`.
- 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.
+ Special cases are :class:`~django.forms.CheckboxInput`,
+ :class:`~django.forms.CheckboxSelectMultiple`, and
+ :class:`~django.forms.SelectMultiple`, which always return
+ ``False`` because an unchecked checkbox and unselected
+ ``<select multiple>`` don't appear in the data of an HTML form
+ submission, so it's unknown whether or not the user submitted a value.
.. method:: use_required_attribute(initial)
diff --git a/docs/releases/1.10.7.txt b/docs/releases/1.10.7.txt
index de9a511f07..9d4f79f5bf 100644
--- a/docs/releases/1.10.7.txt
+++ b/docs/releases/1.10.7.txt
@@ -11,3 +11,6 @@ Bugfixes
* Made admin's ``RelatedFieldWidgetWrapper`` use the wrapped widget's
``value_omitted_from_data()`` method (:ticket:`27905`).
+
+* Fixed model form ``default`` fallback for ``SelectMultiple``
+ (:ticket:`27993`).
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index 3ce94c1b0d..1969474b89 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -335,12 +335,14 @@ 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` and
-:class:`~django.forms.CheckboxSelectMultiple` (or any custom widget whose
+:class:`~django.forms.CheckboxInput`,
+:class:`~django.forms.CheckboxSelectMultiple`, or
+:class:`~django.forms.SelectMultiple` (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
-and want the default fallback for a :class:`~django.db.models.BooleanField`.
+``False``) since an unchecked checkbox and unselected ``<select multiple>``
+don't appear in the data of an HTML form submission. Use a custom form field or
+widget if you're designing an API and want the default fallback behavior for a
+field that uses one of these widgets.
This ``save()`` method accepts an optional ``commit`` keyword argument, which
accepts either ``True`` or ``False``. If you call ``save()`` with
diff --git a/tests/forms_tests/widget_tests/test_selectmultiple.py b/tests/forms_tests/widget_tests/test_selectmultiple.py
index 41dd6a4eb7..71259b9847 100644
--- a/tests/forms_tests/widget_tests/test_selectmultiple.py
+++ b/tests/forms_tests/widget_tests/test_selectmultiple.py
@@ -123,3 +123,8 @@ class SelectMultipleTest(WidgetTest):
</optgroup>
</select>"""
))
+
+ 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 3509146856..c85eb2a6fa 100644
--- a/tests/model_forms/tests.py
+++ b/tests/model_forms/tests.py
@@ -614,6 +614,22 @@ class ModelFormBaseTest(TestCase):
self.assertEqual(m1.mode, '')
self.assertEqual(m1._meta.get_field('mode').get_default(), 'di')
+ def test_default_not_populated_on_selectmultiple(self):
+ class PubForm(forms.ModelForm):
+ mode = forms.CharField(required=False, widget=forms.SelectMultiple)
+
+ class Meta:
+ model = PublicationDefaults
+ fields = ('mode',)
+
+ # Empty data doesn't use the model default because an unselected
+ # SelectMultiple 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'