summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
Diffstat (limited to 'django')
-rw-r--r--django/contrib/postgres/forms/array.py6
-rw-r--r--django/forms/models.py4
-rw-r--r--django/forms/widgets.py27
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']