diff --git a/django/forms/boundfield.py b/django/forms/boundfield.py index e0ee6b4e78..1b5cbfc47c 100644 --- a/django/forms/boundfield.py +++ b/django/forms/boundfield.py @@ -134,18 +134,7 @@ class BoundField(object): the form is not bound or the data otherwise. """ if not self.form.is_bound: - data = self.form.initial.get(self.name, self.field.initial) - if callable(data): - if self._initial_value is not UNSET: - data = self._initial_value - else: - data = data() - # If this is an auto-generated default date, nix the - # microseconds for standardized handling. See #22502. - if (isinstance(data, (datetime.datetime, datetime.time)) and - not self.field.widget.supports_microseconds): - data = data.replace(microsecond=0) - self._initial_value = data + data = self.initial else: data = self.field.bound_data( self.data, self.form.initial.get(self.name, self.field.initial) @@ -231,11 +220,27 @@ class BoundField(object): id_ = widget.attrs.get('id') or self.auto_id return widget.id_for_label(id_) + @property + def initial(self): + data = self.form.initial.get(self.name, self.field.initial) + if callable(data): + if self._initial_value is not UNSET: + data = self._initial_value + else: + data = data() + # If this is an auto-generated default date, nix the + # microseconds for standardized handling. See #22502. + if (isinstance(data, (datetime.datetime, datetime.time)) and + not self.field.widget.supports_microseconds): + data = data.replace(microsecond=0) + self._initial_value = data + return data + def build_widget_attrs(self, attrs, widget=None): if not widget: widget = self.field.widget attrs = dict(attrs) # Copy attrs to avoid modifying the argument. - if not widget.is_hidden and self.field.required and self.form.use_required_attribute: + if widget.use_required_attribute(self.initial) and self.field.required and self.form.use_required_attribute: attrs['required'] = True if self.field.disabled: attrs['disabled'] = True diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 13d22672bf..d6b84c10ab 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -248,6 +248,9 @@ class Widget(six.with_metaclass(RenameWidgetMethods)): """ return id_ + def use_required_attribute(self, initial): + return not self.is_hidden + class Input(Widget): """ @@ -429,6 +432,9 @@ class ClearableFileInput(FileInput): return False return upload + def use_required_attribute(self, initial): + return super(ClearableFileInput, self).use_required_attribute(initial) and not initial + class Textarea(Widget): def __init__(self, attrs=None): @@ -795,12 +801,10 @@ class CheckboxSelectMultiple(RendererMixin, SelectMultiple): renderer = CheckboxFieldRenderer _empty_value = [] - def build_attrs(self, extra_attrs=None, **kwargs): - attrs = super(CheckboxSelectMultiple, self).build_attrs(extra_attrs, **kwargs) - # Remove the 'required' attribute because browser validation would + def use_required_attribute(self, initial): + # Don't use the 'required' attribute because browser validation would # require all checkboxes to be checked instead of at least one. - attrs.pop('required', None) - return attrs + return False class MultiWidget(Widget): diff --git a/docs/releases/1.10.1.txt b/docs/releases/1.10.1.txt index 2ebd36cf0f..b834915701 100644 --- a/docs/releases/1.10.1.txt +++ b/docs/releases/1.10.1.txt @@ -45,3 +45,6 @@ Bugfixes * Fixed crash of ``django.views.static.serve()`` with ``show_indexes`` enabled (:ticket:`26973`). + +* Fixed ``ClearableFileInput`` to avoid the ``required`` HTML attribute when + initial data exists (:ticket:`27037`). diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index 773b89a7bc..0e0ea9f1c2 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -2360,6 +2360,15 @@ Password: '