From 83331d1ed0fde144767e8f35f2d9e512bdd2afb1 Mon Sep 17 00:00:00 2001 From: heathervm Date: Fri, 31 Mar 2017 07:10:08 -0700 Subject: [PATCH] [1.10.x] Fixed #27993 -- Fixed model form default fallback for SelectMultiple. Backport of 7d1e23775344cc3dead03bd4af45f4fdf134b819 from master --- django/forms/widgets.py | 5 +++++ docs/ref/forms/widgets.txt | 11 ++++++----- docs/releases/1.10.7.txt | 3 +++ docs/topics/forms/modelforms.txt | 12 +++++++----- .../widget_tests/test_selectmultiple.py | 5 +++++ tests/model_forms/tests.py | 16 ++++++++++++++++ 6 files changed, 42 insertions(+), 10 deletions(-) diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 179341636c1..3fc1026b6c4 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -631,6 +631,11 @@ class SelectMultiple(Select): return data.getlist(name) return data.get(name) + def value_omitted_from_data(self, data, files, name): + # An unselected `` 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 de9a511f075..9d4f79f5bf4 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 768dc3938ca..8f9828641a5 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 ``""" )) + + 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 9ca329e7594..afb58136805 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -601,6 +601,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'