Fixed #32920 -- Changed BaseForm to access its values through bound fields.
This commit is contained in:
parent
84400d2e9d
commit
90a33ab2ce
|
@ -143,7 +143,13 @@ class BaseForm:
|
||||||
'fields': ';'.join(self.fields),
|
'fields': ';'.join(self.fields),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _bound_items(self):
|
||||||
|
"""Yield (name, bf) pairs, where bf is a BoundField object."""
|
||||||
|
for name in self.fields:
|
||||||
|
yield name, self[name]
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
"""Yield the form's fields as BoundField objects."""
|
||||||
for name in self.fields:
|
for name in self.fields:
|
||||||
yield self[name]
|
yield self[name]
|
||||||
|
|
||||||
|
@ -206,9 +212,9 @@ class BaseForm:
|
||||||
top_errors = self.non_field_errors().copy()
|
top_errors = self.non_field_errors().copy()
|
||||||
output, hidden_fields = [], []
|
output, hidden_fields = [], []
|
||||||
|
|
||||||
for name, field in self.fields.items():
|
for name, bf in self._bound_items():
|
||||||
|
field = bf.field
|
||||||
html_class_attr = ''
|
html_class_attr = ''
|
||||||
bf = self[name]
|
|
||||||
bf_errors = self.error_class(bf.errors)
|
bf_errors = self.error_class(bf.errors)
|
||||||
if bf.is_hidden:
|
if bf.is_hidden:
|
||||||
if bf_errors:
|
if bf_errors:
|
||||||
|
@ -387,15 +393,12 @@ class BaseForm:
|
||||||
self._post_clean()
|
self._post_clean()
|
||||||
|
|
||||||
def _clean_fields(self):
|
def _clean_fields(self):
|
||||||
for name, field in self.fields.items():
|
for name, bf in self._bound_items():
|
||||||
if field.disabled:
|
field = bf.field
|
||||||
value = self.get_initial_for_field(field, name)
|
value = bf.initial if field.disabled else bf.data
|
||||||
else:
|
|
||||||
value = self._field_data_value(field, self.add_prefix(name))
|
|
||||||
try:
|
try:
|
||||||
if isinstance(field, FileField):
|
if isinstance(field, FileField):
|
||||||
initial = self.get_initial_for_field(field, name)
|
value = field.clean(value, bf.initial)
|
||||||
value = field.clean(value, initial)
|
|
||||||
else:
|
else:
|
||||||
value = field.clean(value)
|
value = field.clean(value)
|
||||||
self.cleaned_data[name] = value
|
self.cleaned_data[name] = value
|
||||||
|
@ -437,24 +440,23 @@ class BaseForm:
|
||||||
@cached_property
|
@cached_property
|
||||||
def changed_data(self):
|
def changed_data(self):
|
||||||
data = []
|
data = []
|
||||||
for name, field in self.fields.items():
|
for name, bf in self._bound_items():
|
||||||
data_value = self._field_data_value(field, self.add_prefix(name))
|
field = bf.field
|
||||||
if not field.show_hidden_initial:
|
if not field.show_hidden_initial:
|
||||||
# Use the BoundField's initial as this is the value passed to
|
# Use the BoundField's initial as this is the value passed to
|
||||||
# the widget.
|
# the widget.
|
||||||
initial_value = self[name].initial
|
initial_value = bf.initial
|
||||||
else:
|
else:
|
||||||
initial_prefixed_name = self.add_initial_prefix(name)
|
|
||||||
hidden_widget = field.hidden_widget()
|
hidden_widget = field.hidden_widget()
|
||||||
try:
|
try:
|
||||||
initial_value = field.to_python(
|
initial_value = field.to_python(
|
||||||
self._widget_data_value(hidden_widget, initial_prefixed_name)
|
self._widget_data_value(hidden_widget, bf.html_initial_name)
|
||||||
)
|
)
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
# Always assume data has changed if validation fails.
|
# Always assume data has changed if validation fails.
|
||||||
data.append(name)
|
data.append(name)
|
||||||
continue
|
continue
|
||||||
if field.has_changed(initial_value, data_value):
|
if field.has_changed(initial_value, bf.data):
|
||||||
data.append(name)
|
data.append(name)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
|
@ -2112,15 +2112,47 @@ Password: <input type="password" name="password" required></li>
|
||||||
self.assertEqual(unbound['hi_without_microsec'].value(), now_no_ms)
|
self.assertEqual(unbound['hi_without_microsec'].value(), now_no_ms)
|
||||||
self.assertEqual(unbound['ti_without_microsec'].value(), now_no_ms)
|
self.assertEqual(unbound['ti_without_microsec'].value(), now_no_ms)
|
||||||
|
|
||||||
def test_datetime_clean_initial_callable_disabled(self):
|
def get_datetime_form_with_callable_initial(self, disabled, microseconds=0):
|
||||||
now = datetime.datetime(2006, 10, 25, 14, 30, 45, 123456)
|
class FakeTime:
|
||||||
|
def __init__(self):
|
||||||
|
self.elapsed_seconds = 0
|
||||||
|
|
||||||
|
def now(self):
|
||||||
|
self.elapsed_seconds += 1
|
||||||
|
return datetime.datetime(
|
||||||
|
2006, 10, 25, 14, 30, 45 + self.elapsed_seconds,
|
||||||
|
microseconds,
|
||||||
|
)
|
||||||
|
|
||||||
class DateTimeForm(forms.Form):
|
class DateTimeForm(forms.Form):
|
||||||
dt = DateTimeField(initial=lambda: now, disabled=True)
|
dt = DateTimeField(initial=FakeTime().now, disabled=disabled)
|
||||||
|
|
||||||
form = DateTimeForm({})
|
return DateTimeForm({})
|
||||||
|
|
||||||
|
def test_datetime_clean_disabled_callable_initial_microseconds(self):
|
||||||
|
"""
|
||||||
|
Cleaning a form with a disabled DateTimeField and callable initial
|
||||||
|
removes microseconds.
|
||||||
|
"""
|
||||||
|
form = self.get_datetime_form_with_callable_initial(
|
||||||
|
disabled=True, microseconds=123456,
|
||||||
|
)
|
||||||
self.assertEqual(form.errors, {})
|
self.assertEqual(form.errors, {})
|
||||||
self.assertEqual(form.cleaned_data, {'dt': now})
|
self.assertEqual(form.cleaned_data, {
|
||||||
|
'dt': datetime.datetime(2006, 10, 25, 14, 30, 46),
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_datetime_clean_disabled_callable_initial_bound_field(self):
|
||||||
|
"""
|
||||||
|
The cleaned value for a form with a disabled DateTimeField and callable
|
||||||
|
initial matches the bound field's cached initial value.
|
||||||
|
"""
|
||||||
|
form = self.get_datetime_form_with_callable_initial(disabled=True)
|
||||||
|
self.assertEqual(form.errors, {})
|
||||||
|
cleaned = form.cleaned_data['dt']
|
||||||
|
self.assertEqual(cleaned, datetime.datetime(2006, 10, 25, 14, 30, 46))
|
||||||
|
bf = form['dt']
|
||||||
|
self.assertEqual(cleaned, bf.initial)
|
||||||
|
|
||||||
def test_datetime_changed_data_callable_with_microseconds(self):
|
def test_datetime_changed_data_callable_with_microseconds(self):
|
||||||
class DateTimeForm(forms.Form):
|
class DateTimeForm(forms.Form):
|
||||||
|
|
Loading…
Reference in New Issue