Skip to content

Commit a5de0df

Browse files
melinathclaudep
authored andcommitted
Fixed #22502 -- Fixed microseconds/default/form interaction
Made explicit lack of microsecond handling by built-in datetime form fields. Used that explicitness to appropriately nix microsecond values in bound fields. Thanks Claude Paroz for the review.
1 parent 35e1b1e commit a5de0df

File tree

4 files changed

+35
-3
lines changed

4 files changed

+35
-3
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ answer newbie questions, and generally made Django that much better:
137137
138138
Jonathan Buchanan <[email protected]>
139139
Jacob Burch <[email protected]>
140+
Stephen Burrows <[email protected]>
140141
Max Burstein <http://maxburstein.com>
141142
Keith Bussell <[email protected]>
142143
C8E

django/forms/forms.py

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from collections import OrderedDict
88
import copy
9+
import datetime
910
import warnings
1011

1112
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
@@ -593,6 +594,11 @@ def value(self):
593594
data = self.form.initial.get(self.name, self.field.initial)
594595
if callable(data):
595596
data = data()
597+
# If this is an auto-generated default date, nix the
598+
# microseconds for standardized handling. See #22502.
599+
if (isinstance(data, (datetime.datetime, datetime.time)) and
600+
not getattr(self.field.widget, 'supports_microseconds', True)):
601+
data = data.replace(microsecond=0)
596602
else:
597603
data = self.field.bound_data(
598604
self.data, self.form.initial.get(self.name, self.field.initial)

django/forms/widgets.py

+2
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ def render(self, name, value, attrs=None):
419419

420420
class DateTimeBaseInput(TextInput):
421421
format_key = ''
422+
supports_microseconds = False
422423

423424
def __init__(self, attrs=None, format=None):
424425
super(DateTimeBaseInput, self).__init__(attrs)
@@ -846,6 +847,7 @@ class SplitDateTimeWidget(MultiWidget):
846847
"""
847848
A Widget that splits datetime input into two <input type="text"> boxes.
848849
"""
850+
supports_microseconds = False
849851

850852
def __init__(self, attrs=None, date_format=None, time_format=None):
851853
widgets = (DateInput(attrs=attrs, format=date_format),

tests/forms_tests/tests/test_forms.py

+26-3
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
from django.core.validators import RegexValidator
1212
from django.forms import (
1313
BooleanField, CharField, CheckboxSelectMultiple, ChoiceField, DateField,
14-
EmailField, FileField, FloatField, Form, forms, HiddenInput, IntegerField,
15-
MultipleChoiceField, MultipleHiddenInput, MultiValueField,
14+
DateTimeField, EmailField, FileField, FloatField, Form, forms, HiddenInput,
15+
IntegerField, MultipleChoiceField, MultipleHiddenInput, MultiValueField,
1616
NullBooleanField, PasswordInput, RadioSelect, Select, SplitDateTimeField,
17-
Textarea, TextInput, ValidationError, widgets,
17+
Textarea, TextInput, TimeField, ValidationError, widgets
1818
)
1919
from django.forms.utils import ErrorList
2020
from django.http import QueryDict
@@ -1321,6 +1321,29 @@ class UserRegistration(Form):
13211321
self.assertEqual(bound['password'].value(), 'foo')
13221322
self.assertEqual(unbound['password'].value(), None)
13231323

1324+
def test_initial_datetime_values(self):
1325+
now = datetime.datetime.now()
1326+
# Nix microseconds (since they should be ignored). #22502
1327+
now_no_ms = now.replace(microsecond=0)
1328+
if now == now_no_ms:
1329+
now = now.replace(microsecond=1)
1330+
1331+
def delayed_now():
1332+
return now
1333+
1334+
def delayed_now_time():
1335+
return now.time()
1336+
1337+
class DateTimeForm(Form):
1338+
auto_timestamp = DateTimeField(initial=delayed_now)
1339+
auto_time_only = TimeField(initial=delayed_now_time)
1340+
supports_microseconds = DateTimeField(initial=delayed_now, widget=TextInput)
1341+
1342+
unbound = DateTimeForm()
1343+
self.assertEqual(unbound['auto_timestamp'].value(), now_no_ms)
1344+
self.assertEqual(unbound['auto_time_only'].value(), now_no_ms.time())
1345+
self.assertEqual(unbound['supports_microseconds'].value(), now)
1346+
13241347
def test_help_text(self):
13251348
# You can specify descriptive text for a field by using the 'help_text' argument)
13261349
class UserRegistration(Form):

0 commit comments

Comments
 (0)