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.
This commit is contained in:
Stephen Burrows 2014-05-06 18:14:06 -07:00 committed by Claude Paroz
parent 35e1b1efab
commit a5de0df58b
4 changed files with 35 additions and 3 deletions

View File

@ -137,6 +137,7 @@ answer newbie questions, and generally made Django that much better:
btoll@bestweb.net btoll@bestweb.net
Jonathan Buchanan <jonathan.buchanan@gmail.com> Jonathan Buchanan <jonathan.buchanan@gmail.com>
Jacob Burch <jacobburch@gmail.com> Jacob Burch <jacobburch@gmail.com>
Stephen Burrows <stephen.r.burrows@gmail.com>
Max Burstein <http://maxburstein.com> Max Burstein <http://maxburstein.com>
Keith Bussell <kbussell@gmail.com> Keith Bussell <kbussell@gmail.com>
C8E C8E

View File

@ -6,6 +6,7 @@ from __future__ import unicode_literals
from collections import OrderedDict from collections import OrderedDict
import copy import copy
import datetime
import warnings import warnings
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
@ -593,6 +594,11 @@ class BoundField(object):
data = self.form.initial.get(self.name, self.field.initial) data = self.form.initial.get(self.name, self.field.initial)
if callable(data): if callable(data):
data = data() 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 getattr(self.field.widget, 'supports_microseconds', True)):
data = data.replace(microsecond=0)
else: else:
data = self.field.bound_data( data = self.field.bound_data(
self.data, self.form.initial.get(self.name, self.field.initial) self.data, self.form.initial.get(self.name, self.field.initial)

View File

@ -419,6 +419,7 @@ class Textarea(Widget):
class DateTimeBaseInput(TextInput): class DateTimeBaseInput(TextInput):
format_key = '' format_key = ''
supports_microseconds = False
def __init__(self, attrs=None, format=None): def __init__(self, attrs=None, format=None):
super(DateTimeBaseInput, self).__init__(attrs) super(DateTimeBaseInput, self).__init__(attrs)
@ -846,6 +847,7 @@ class SplitDateTimeWidget(MultiWidget):
""" """
A Widget that splits datetime input into two <input type="text"> boxes. A Widget that splits datetime input into two <input type="text"> boxes.
""" """
supports_microseconds = False
def __init__(self, attrs=None, date_format=None, time_format=None): def __init__(self, attrs=None, date_format=None, time_format=None):
widgets = (DateInput(attrs=attrs, format=date_format), widgets = (DateInput(attrs=attrs, format=date_format),

View File

@ -11,10 +11,10 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.forms import ( from django.forms import (
BooleanField, CharField, CheckboxSelectMultiple, ChoiceField, DateField, BooleanField, CharField, CheckboxSelectMultiple, ChoiceField, DateField,
EmailField, FileField, FloatField, Form, forms, HiddenInput, IntegerField, DateTimeField, EmailField, FileField, FloatField, Form, forms, HiddenInput,
MultipleChoiceField, MultipleHiddenInput, MultiValueField, IntegerField, MultipleChoiceField, MultipleHiddenInput, MultiValueField,
NullBooleanField, PasswordInput, RadioSelect, Select, SplitDateTimeField, NullBooleanField, PasswordInput, RadioSelect, Select, SplitDateTimeField,
Textarea, TextInput, ValidationError, widgets, Textarea, TextInput, TimeField, ValidationError, widgets
) )
from django.forms.utils import ErrorList from django.forms.utils import ErrorList
from django.http import QueryDict from django.http import QueryDict
@ -1321,6 +1321,29 @@ class FormsTestCase(TestCase):
self.assertEqual(bound['password'].value(), 'foo') self.assertEqual(bound['password'].value(), 'foo')
self.assertEqual(unbound['password'].value(), None) self.assertEqual(unbound['password'].value(), None)
def test_initial_datetime_values(self):
now = datetime.datetime.now()
# Nix microseconds (since they should be ignored). #22502
now_no_ms = now.replace(microsecond=0)
if now == now_no_ms:
now = now.replace(microsecond=1)
def delayed_now():
return now
def delayed_now_time():
return now.time()
class DateTimeForm(Form):
auto_timestamp = DateTimeField(initial=delayed_now)
auto_time_only = TimeField(initial=delayed_now_time)
supports_microseconds = DateTimeField(initial=delayed_now, widget=TextInput)
unbound = DateTimeForm()
self.assertEqual(unbound['auto_timestamp'].value(), now_no_ms)
self.assertEqual(unbound['auto_time_only'].value(), now_no_ms.time())
self.assertEqual(unbound['supports_microseconds'].value(), now)
def test_help_text(self): def test_help_text(self):
# You can specify descriptive text for a field by using the 'help_text' argument) # You can specify descriptive text for a field by using the 'help_text' argument)
class UserRegistration(Form): class UserRegistration(Form):