Fixed #32924 -- Changed BaseForm.get_initial_for_field() to remove microseconds when needed.
This commit is contained in:
parent
788441c6ab
commit
0dc25526d8
|
@ -1,4 +1,3 @@
|
||||||
import datetime
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
@ -228,13 +227,7 @@ class BoundField:
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def initial(self):
|
def initial(self):
|
||||||
data = self.form.get_initial_for_field(self.field, self.name)
|
return self.form.get_initial_for_field(self.field, self.name)
|
||||||
# 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)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def build_widget_attrs(self, attrs, widget=None):
|
def build_widget_attrs(self, attrs, widget=None):
|
||||||
widget = widget or self.field.widget
|
widget = widget or self.field.widget
|
||||||
|
|
|
@ -3,6 +3,7 @@ Form classes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import datetime
|
||||||
|
|
||||||
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
|
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
|
||||||
from django.forms.fields import Field, FileField
|
from django.forms.fields import Field, FileField
|
||||||
|
@ -475,6 +476,11 @@ class BaseForm:
|
||||||
value = self.initial.get(field_name, field.initial)
|
value = self.initial.get(field_name, field.initial)
|
||||||
if callable(value):
|
if callable(value):
|
||||||
value = value()
|
value = value()
|
||||||
|
# If this is an auto-generated default date, nix the microseconds
|
||||||
|
# for standardized handling. See #22502.
|
||||||
|
if (isinstance(value, (datetime.datetime, datetime.time)) and
|
||||||
|
not field.widget.supports_microseconds):
|
||||||
|
value = value.replace(microsecond=0)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1983,11 +1983,15 @@ Password: <input type="password" name="password" required></li>
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_get_initial_for_field(self):
|
def test_get_initial_for_field(self):
|
||||||
|
now = datetime.datetime(2006, 10, 25, 14, 30, 45, 123456)
|
||||||
|
|
||||||
class PersonForm(Form):
|
class PersonForm(Form):
|
||||||
first_name = CharField(initial='John')
|
first_name = CharField(initial='John')
|
||||||
last_name = CharField(initial='Doe')
|
last_name = CharField(initial='Doe')
|
||||||
age = IntegerField()
|
age = IntegerField()
|
||||||
occupation = CharField(initial=lambda: 'Unknown')
|
occupation = CharField(initial=lambda: 'Unknown')
|
||||||
|
dt_fixed = DateTimeField(initial=now)
|
||||||
|
dt_callable = DateTimeField(initial=lambda: now)
|
||||||
|
|
||||||
form = PersonForm(initial={'first_name': 'Jane'})
|
form = PersonForm(initial={'first_name': 'Jane'})
|
||||||
cases = [
|
cases = [
|
||||||
|
@ -1997,6 +2001,9 @@ Password: <input type="password" name="password" required></li>
|
||||||
('first_name', 'Jane'),
|
('first_name', 'Jane'),
|
||||||
# Callables are evaluated.
|
# Callables are evaluated.
|
||||||
('occupation', 'Unknown'),
|
('occupation', 'Unknown'),
|
||||||
|
# Microseconds are removed from datetimes.
|
||||||
|
('dt_fixed', datetime.datetime(2006, 10, 25, 14, 30, 45)),
|
||||||
|
('dt_callable', datetime.datetime(2006, 10, 25, 14, 30, 45)),
|
||||||
]
|
]
|
||||||
for field_name, expected in cases:
|
for field_name, expected in cases:
|
||||||
with self.subTest(field_name=field_name):
|
with self.subTest(field_name=field_name):
|
||||||
|
@ -2104,6 +2111,8 @@ Password: <input type="password" name="password" required></li>
|
||||||
supports_microseconds = False
|
supports_microseconds = False
|
||||||
|
|
||||||
class DateTimeForm(Form):
|
class DateTimeForm(Form):
|
||||||
|
# Test a non-callable.
|
||||||
|
fixed = DateTimeField(initial=now)
|
||||||
auto_timestamp = DateTimeField(initial=delayed_now)
|
auto_timestamp = DateTimeField(initial=delayed_now)
|
||||||
auto_time_only = TimeField(initial=delayed_now_time)
|
auto_time_only = TimeField(initial=delayed_now_time)
|
||||||
supports_microseconds = DateTimeField(initial=delayed_now, widget=TextInput)
|
supports_microseconds = DateTimeField(initial=delayed_now, widget=TextInput)
|
||||||
|
@ -2113,6 +2122,7 @@ Password: <input type="password" name="password" required></li>
|
||||||
|
|
||||||
unbound = DateTimeForm()
|
unbound = DateTimeForm()
|
||||||
cases = [
|
cases = [
|
||||||
|
('fixed', now_no_ms),
|
||||||
('auto_timestamp', now_no_ms),
|
('auto_timestamp', now_no_ms),
|
||||||
('auto_time_only', now_no_ms.time()),
|
('auto_time_only', now_no_ms.time()),
|
||||||
('supports_microseconds', now),
|
('supports_microseconds', now),
|
||||||
|
@ -2124,6 +2134,10 @@ Password: <input type="password" name="password" required></li>
|
||||||
with self.subTest(field_name=field_name):
|
with self.subTest(field_name=field_name):
|
||||||
actual = unbound[field_name].value()
|
actual = unbound[field_name].value()
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
# Also check get_initial_for_field().
|
||||||
|
field = unbound.fields[field_name]
|
||||||
|
actual = unbound.get_initial_for_field(field, field_name)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
def get_datetime_form_with_callable_initial(self, disabled, microseconds=0):
|
def get_datetime_form_with_callable_initial(self, disabled, microseconds=0):
|
||||||
class FakeTime:
|
class FakeTime:
|
||||||
|
|
Loading…
Reference in New Issue