Fixed #8962 -- Consistently support format and input_format in the various (individual, combined, split) date and time form fields and widgets.

Many thanks to Tai Lee for doing all the work here.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@10115 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Karen Tracey 2009-03-22 16:13:06 +00:00
parent b203db6ec8
commit 14b160957e
8 changed files with 177 additions and 17 deletions

View File

@ -256,6 +256,7 @@ answer newbie questions, and generally made Django that much better:
Eugene Lazutkin <http://lazutkin.com/blog/>
lcordier@point45.com
Jeong-Min Lee <falsetru@gmail.com>
Tai Lee <real.human@mrmachine.net>
Jannis Leidel <jl@websushi.org>
Christopher Lenz <http://www.cmlenz.net/>
lerouxb@gmail.com
@ -295,7 +296,6 @@ answer newbie questions, and generally made Django that much better:
Aljosa Mohorovic <aljosa.mohorovic@gmail.com>
Ramiro Morales <rm0@gmx.net>
Eric Moritz <http://eric.themoritzfamily.com/>
mrmachine <real.human@mrmachine.net>
Robin Munn <http://www.geekforgod.com/>
James Murty
msundstr

View File

@ -36,3 +36,13 @@ class DateTimeField(forms.DateTimeField):
def __init__(self, input_formats=None, *args, **kwargs):
input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
super(DateTimeField, self).__init__(input_formats=input_formats, *args, **kwargs)
class SplitDateTimeField(forms.SplitDateTimeField):
"""
Split date and time input fields which use non-US date and time input
formats by default.
"""
def __init__(self, input_date_formats=None, input_time_formats=None, *args, **kwargs):
input_date_formats = input_date_formats or DEFAULT_DATE_INPUT_FORMATS
super(SplitDateTimeField, self).__init__(input_date_formats=input_date_formats,
input_time_formats=input_time_formats, *args, **kwargs)

View File

@ -28,7 +28,7 @@ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode, smart_str
from util import ErrorList, ValidationError
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget
from django.core.files.uploadedfile import SimpleUploadedFile as UploadedFile
__all__ = (
@ -283,6 +283,7 @@ DEFAULT_DATE_INPUT_FORMATS = (
)
class DateField(Field):
widget = DateInput
default_error_messages = {
'invalid': _(u'Enter a valid date.'),
}
@ -850,13 +851,13 @@ class SplitDateTimeField(MultiValueField):
'invalid_time': _(u'Enter a valid time.'),
}
def __init__(self, *args, **kwargs):
def __init__(self, input_date_formats=None, input_time_formats=None, *args, **kwargs):
errors = self.default_error_messages.copy()
if 'error_messages' in kwargs:
errors.update(kwargs['error_messages'])
fields = (
DateField(error_messages={'invalid': errors['invalid_date']}),
TimeField(error_messages={'invalid': errors['invalid_time']}),
DateField(input_formats=input_date_formats, error_messages={'invalid': errors['invalid_date']}),
TimeField(input_formats=input_time_formats, error_messages={'invalid': errors['invalid_time']}),
)
super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)

View File

@ -23,7 +23,7 @@ from urlparse import urljoin
__all__ = (
'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput',
'HiddenInput', 'MultipleHiddenInput',
'FileInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput',
'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput',
'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
'CheckboxSelectMultiple', 'MultiWidget',
'SplitDateTimeWidget',
@ -285,6 +285,23 @@ class Textarea(Widget):
return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs),
conditional_escape(force_unicode(value))))
class DateInput(Input):
input_type = 'text'
format = '%Y-%m-%d' # '2006-10-25'
def __init__(self, attrs=None, format=None):
super(DateInput, self).__init__(attrs)
if format:
self.format = format
def render(self, name, value, attrs=None):
if value is None:
value = ''
elif hasattr(value, 'strftime'):
value = datetime_safe.new_date(value)
value = value.strftime(self.format)
return super(DateInput, self).render(name, value, attrs)
class DateTimeInput(Input):
input_type = 'text'
format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59'
@ -304,12 +321,18 @@ class DateTimeInput(Input):
class TimeInput(Input):
input_type = 'text'
format = '%H:%M:%S' # '14:30:59'
def __init__(self, attrs=None, format=None):
super(TimeInput, self).__init__(attrs)
if format:
self.format = format
def render(self, name, value, attrs=None):
if value is None:
value = ''
elif isinstance(value, time):
value = value.replace(microsecond=0)
elif hasattr(value, 'strftime'):
value = value.strftime(self.format)
return super(TimeInput, self).render(name, value, attrs)
class CheckboxInput(Widget):
@ -654,8 +677,16 @@ class SplitDateTimeWidget(MultiWidget):
"""
A Widget that splits datetime input into two <input type="text"> boxes.
"""
def __init__(self, attrs=None):
widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs))
date_format = DateInput.format
time_format = TimeInput.format
def __init__(self, attrs=None, date_format=None, time_format=None):
if date_format:
self.date_format = date_format
if time_format:
self.time_format = time_format
widgets = (DateInput(attrs=attrs, format=self.date_format),
TimeInput(attrs=attrs, format=self.time_format))
super(SplitDateTimeWidget, self).__init__(widgets, attrs)
def decompress(self, value):
@ -670,4 +701,3 @@ class SplitHiddenDateTimeWidget(SplitDateTimeWidget):
def __init__(self, attrs=None):
widgets = (HiddenInput(attrs=attrs), HiddenInput(attrs=attrs))
super(SplitDateTimeWidget, self).__init__(widgets, attrs)

View File

@ -66,9 +66,9 @@ Countries currently supported by :mod:`~django.contrib.localflavor` are:
* `United States of America`_
The ``django.contrib.localflavor`` package also includes a ``generic`` subpackage,
containing useful code that is not specific to one particular country or
culture. Currently, it defines date and datetime input fields based on those
from :ref:`forms <topics-forms-index>`, but with non-US default formats.
containing useful code that is not specific to one particular country or culture.
Currently, it defines date, datetime and split datetime input fields based on
those from :ref:`forms <topics-forms-index>`, but with non-US default formats.
Here's an example of how to use them::
from django import forms

View File

@ -398,7 +398,7 @@ Takes extra arguments:
.. class:: DateField(**kwargs)
* Default widget: ``TextInput``
* Default widget: ``DateInput``
* Empty value: ``None``
* Normalizes to: A Python ``datetime.date`` object.
* Validates that the given value is either a ``datetime.date``,
@ -420,6 +420,10 @@ If no ``input_formats`` argument is provided, the default input formats are::
'%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
'%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
.. versionchanged:: 1.1
The ``DateField`` previously used a ``TextInput`` widget by default. It now
uses a ``DateInput`` widget.
``DateTimeField``
~~~~~~~~~~~~~~~~~
@ -739,6 +743,36 @@ The following are not yet documented.
.. class:: SplitDateTimeField(**kwargs)
* Default widget: ``SplitDateTimeWidget``
* Empty value: ``None``
* Normalizes to: A Python ``datetime.datetime`` object.
* Validates that the given value is a ``datetime.datetime`` or string
formatted in a particular datetime format.
* Error message keys: ``required``, ``invalid``
Takes two optional arguments:
.. attribute:: SplitDateTimeField.input_date_formats
A list of formats used to attempt to convert a string to a valid
``datetime.date`` object.
If no ``input_date_formats`` argument is provided, the default input formats
for ``DateField`` are used.
.. attribute:: SplitDateTimeField.input_time_formats
A list of formats used to attempt to convert a string to a valid
``datetime.time`` object.
If no ``input_time_formats`` argument is provided, the default input formats
for ``TimeField`` are used.
.. versionchanged:: 1.1
The ``SplitDateTimeField`` previously used two ``TextInput`` widgets by
default. The ``input_date_formats`` and ``input_time_formats`` arguments
are also new.
Fields which handle relationships
---------------------------------

View File

@ -36,12 +36,49 @@ commonly used groups of widgets:
File upload input: ``<input type='file' ...>``
.. class:: DateInput
.. versionadded:: 1.1
Date input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
.. attribute:: DateInput.format
The format in which this field's initial value will be displayed.
If no ``format`` argument is provided, the default format is ``'%Y-%m-%d'``.
.. class:: DateTimeInput
.. versionadded:: 1.0
Date/time input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
.. attribute:: DateTimeInput.format
The format in which this field's initial value will be displayed.
If no ``format`` argument is provided, the default format is ``'%Y-%m-%d %H:%M:%S'``.
.. class:: TimeInput
Time input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
.. attribute:: TimeInput.format
The format in which this field's initial value will be displayed.
If no ``format`` argument is provided, the default format is ``'%H:%M:%S'``.
.. versionchanged:: 1.1
The ``format`` argument was not supported in Django 1.0.
.. class:: Textarea
Text area: ``<textarea>...</textarea>``
@ -91,8 +128,15 @@ commonly used groups of widgets:
.. class:: SplitDateTimeWidget
Wrapper around two ``TextInput`` widgets: one for the date, and one for the
time.
Wrapper around two widgets: ``DateInput`` for the date, and ``TimeInput``
for the time.
Takes two optional arguments, ``date_format`` and ``time_format``, which
work just like the ``format`` argument for ``DateInput`` and ``TimeInput``.
.. versionchanged:: 1.1
The ``date_format`` and ``time_format`` arguments were not supported in Django 1.0.
Specifying widgets
------------------

View File

@ -1071,6 +1071,11 @@ included on both widgets.
>>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30))
u'<input type="text" class="pretty" value="2006-01-10" name="date_0" /><input type="text" class="pretty" value="07:30:00" name="date_1" />'
Use 'date_format' and 'time_format' to change the way a value is displayed.
>>> w = SplitDateTimeWidget(date_format='%d/%m/%Y', time_format='%H:%M')
>>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30))
u'<input type="text" name="date_0" value="10/01/2006" /><input type="text" name="date_1" value="07:30" />'
>>> w._has_changed(datetime.datetime(2008, 5, 5, 12, 40, 00), [u'2008-05-05', u'12:40:00'])
False
>>> w._has_changed(datetime.datetime(2008, 5, 5, 12, 40, 00), [u'2008-05-05', u'12:41:00'])
@ -1093,6 +1098,34 @@ u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
u'<input type="text" name="date" value="2007-09-17 12:51:00" />'
Use 'format' to change the way a value is displayed.
>>> w = DateTimeInput(format='%d/%m/%Y %H:%M')
>>> w.render('date', d)
u'<input type="text" name="date" value="17/09/2007 12:51" />'
# DateInput ###################################################################
>>> w = DateInput()
>>> w.render('date', None)
u'<input type="text" name="date" />'
>>> d = datetime.date(2007, 9, 17)
>>> print d
2007-09-17
>>> w.render('date', d)
u'<input type="text" name="date" value="2007-09-17" />'
>>> w.render('date', datetime.date(2007, 9, 17))
u'<input type="text" name="date" value="2007-09-17" />'
We should be able to initialize from a unicode value.
>>> w.render('date', u'2007-09-17')
u'<input type="text" name="date" value="2007-09-17" />'
Use 'format' to change the way a value is displayed.
>>> w = DateInput(format='%d/%m/%Y')
>>> w.render('date', d)
u'<input type="text" name="date" value="17/09/2007" />'
# TimeInput ###################################################################
>>> w = TimeInput()
@ -1114,6 +1147,11 @@ We should be able to initialize from a unicode value.
>>> w.render('time', u'13:12:11')
u'<input type="text" name="time" value="13:12:11" />'
Use 'format' to change the way a value is displayed.
>>> w = TimeInput(format='%H:%M')
>>> w.render('time', t)
u'<input type="text" name="time" value="12:51" />'
# SplitHiddenDateTimeWidget ###################################################
>>> from django.forms.widgets import SplitHiddenDateTimeWidget
@ -1121,6 +1159,9 @@ u'<input type="text" name="time" value="13:12:11" />'
>>> w = SplitHiddenDateTimeWidget()
>>> w.render('date', '')
u'<input type="hidden" name="date_0" /><input type="hidden" name="date_1" />'
>>> d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548)
>>> print d
2007-09-17 12:51:34.482548
>>> w.render('date', d)
u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:34" />'
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51, 34))