diff options
Diffstat (limited to 'django')
| -rw-r--r-- | django/contrib/postgres/forms/array.py | 6 | ||||
| -rw-r--r-- | django/forms/models.py | 4 | ||||
| -rw-r--r-- | django/forms/widgets.py | 27 |
3 files changed, 31 insertions, 6 deletions
diff --git a/django/contrib/postgres/forms/array.py b/django/contrib/postgres/forms/array.py index dd9ac1178e..bd3daaae0b 100644 --- a/django/contrib/postgres/forms/array.py +++ b/django/contrib/postgres/forms/array.py @@ -103,6 +103,12 @@ class SplitArrayWidget(forms.Widget): return [self.widget.value_from_datadict(data, files, '%s_%s' % (name, index)) for index in range(self.size)] + def value_omitted_from_data(self, data, files, name): + return all( + self.widget.value_omitted_from_data(data, files, '%s_%s' % (name, index)) + for index in range(self.size) + ) + def id_for_label(self, id_): # See the comment for RadioSelect.id_for_label() if id_: diff --git a/django/forms/models.py b/django/forms/models.py index 1d54ea9676..ce93dead9e 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -54,8 +54,8 @@ def construct_instance(form, instance, fields=None, exclude=None): continue # Leave defaults for fields that aren't in POST data, except for # checkbox inputs because they don't appear in POST data if not checked. - if (f.has_default() and form.add_prefix(f.name) not in form.data and - not getattr(form[f.name].field.widget, 'dont_use_model_field_default_for_empty_data', False)): + if (f.has_default() and + form[f.name].field.widget.value_omitted_from_data(form.data, form.files, form.add_prefix(f.name))): continue # Defer saving file-type fields until after the other fields, so a # callable upload_to can use the values from other fields. diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 735207aa80..965f84f7df 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -237,6 +237,9 @@ class Widget(six.with_metaclass(RenameWidgetMethods)): """ return data.get(name) + def value_omitted_from_data(self, data, files, name): + return name not in data + def id_for_label(self, id_): """ Returns the HTML ID attribute of this Widget for use by a <label>, @@ -350,6 +353,9 @@ class FileInput(Input): "File widgets take data from FILES, not POST" return files.get(name) + def value_omitted_from_data(self, data, files, name): + return name not in files + FILE_INPUT_CONTRADICTION = object() @@ -480,10 +486,6 @@ def boolean_check(v): class CheckboxInput(Widget): - # Don't use model field defaults for fields that aren't in POST data, - # because checkboxes don't appear in POST data if not checked. - dont_use_model_field_default_for_empty_data = True - def __init__(self, attrs=None, check_test=None): super(CheckboxInput, self).__init__(attrs) # check_test is a callable that takes a value and returns True @@ -511,6 +513,11 @@ class CheckboxInput(Widget): value = values.get(value.lower(), value) return bool(value) + 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 Select(Widget): allow_multiple_selected = False @@ -871,6 +878,12 @@ class MultiWidget(Widget): def value_from_datadict(self, data, files, name): return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)] + def value_omitted_from_data(self, data, files, name): + return all( + widget.value_omitted_from_data(data, files, name + '_%s' % i) + for i, widget in enumerate(self.widgets) + ) + def format_output(self, rendered_widgets): """ Given a list of rendered widgets (as strings), returns a Unicode string @@ -1056,6 +1069,12 @@ class SelectDateWidget(Widget): return '%s-%s-%s' % (y, m, d) return data.get(name) + def value_omitted_from_data(self, data, files, name): + return not any( + ('{}_{}'.format(name, interval) in data) + for interval in ('year', 'month', 'day') + ) + def create_select(self, name, field, value, val, choices, none_value): if 'id' in self.attrs: id_ = self.attrs['id'] |
