diff --git a/django/forms/fields.py b/django/forms/fields.py
index 7f0d26d1aa..124e4f669a 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -199,7 +199,7 @@ class CharField(Field):
def widget_attrs(self, widget):
attrs = super(CharField, self).widget_attrs(widget)
- if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
+ if self.max_length is not None and isinstance(widget, TextInput):
# The HTML attribute is maxlength, not max_length.
attrs.update({'maxlength': str(self.max_length)})
return attrs
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 7651efccd0..763da0cff2 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -260,10 +260,17 @@ class Input(Widget):
final_attrs['value'] = force_text(self._format_value(value))
return format_html('', flatatt(final_attrs))
+
class TextInput(Input):
input_type = 'text'
-class PasswordInput(Input):
+ def __init__(self, attrs=None):
+ if attrs is not None:
+ self.input_type = attrs.pop('type', self.input_type)
+ super(TextInput, self).__init__(attrs)
+
+
+class PasswordInput(TextInput):
input_type = 'password'
def __init__(self, attrs=None, render_value=False):
@@ -400,9 +407,8 @@ class Textarea(Widget):
flatatt(final_attrs),
force_text(value))
-class DateInput(Input):
- input_type = 'text'
+class DateInput(TextInput):
def __init__(self, attrs=None, format=None):
super(DateInput, self).__init__(attrs)
if format:
@@ -431,9 +437,8 @@ class DateInput(Input):
pass
return super(DateInput, self)._has_changed(self._format_value(initial), data)
-class DateTimeInput(Input):
- input_type = 'text'
+class DateTimeInput(TextInput):
def __init__(self, attrs=None, format=None):
super(DateTimeInput, self).__init__(attrs)
if format:
@@ -462,9 +467,8 @@ class DateTimeInput(Input):
pass
return super(DateTimeInput, self)._has_changed(self._format_value(initial), data)
-class TimeInput(Input):
- input_type = 'text'
+class TimeInput(TextInput):
def __init__(self, attrs=None, format=None):
super(TimeInput, self).__init__(attrs)
if format:
diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
index eab314a4cd..1935cb23bc 100644
--- a/docs/ref/forms/widgets.txt
+++ b/docs/ref/forms/widgets.txt
@@ -126,8 +126,9 @@ provided for each widget will be rendered exactly the same::
On a real Web page, you probably don't want every widget to look the same. You
might want a larger input element for the comment, and you might want the
-'name' widget to have some special CSS class. To do this, you use the
-:attr:`Widget.attrs` argument when creating the widget:
+'name' widget to have some special CSS class. It is also possible to specify
+the 'type' attribute to take advantage of the new HTML5 input types. To do
+this, you use the :attr:`Widget.attrs` argument when creating the widget:
For example::
@@ -245,7 +246,7 @@ commonly used groups of widgets:
Date input as a simple text box: ````
- Takes one optional argument:
+ Takes same arguments as :class:`TextInput`, with one more optional argument:
.. attribute:: DateInput.format
@@ -262,7 +263,7 @@ commonly used groups of widgets:
Date/time input as a simple text box: ````
- Takes one optional argument:
+ Takes same arguments as :class:`TextInput`, with one more optional argument:
.. attribute:: DateTimeInput.format
@@ -279,7 +280,7 @@ commonly used groups of widgets:
Time input as a simple text box: ````
- Takes one optional argument:
+ Takes same arguments as :class:`TextInput`, with one more optional argument:
.. attribute:: TimeInput.format
diff --git a/tests/regressiontests/forms/tests/widgets.py b/tests/regressiontests/forms/tests/widgets.py
index 544ca41642..104144b288 100644
--- a/tests/regressiontests/forms/tests/widgets.py
+++ b/tests/regressiontests/forms/tests/widgets.py
@@ -31,9 +31,9 @@ class FormsWidgetTestCase(TestCase):
self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '')
# You can also pass 'attrs' to the constructor:
- w = TextInput(attrs={'class': 'fun'})
- self.assertHTMLEqual(w.render('email', ''), '')
- self.assertHTMLEqual(w.render('email', 'foo@example.com'), '')
+ w = TextInput(attrs={'class': 'fun', 'type': 'email'})
+ self.assertHTMLEqual(w.render('email', ''), '')
+ self.assertHTMLEqual(w.render('email', 'foo@example.com'), '')
# 'attrs' passed to render() get precedence over those passed to the constructor:
w = TextInput(attrs={'class': 'pretty'})
@@ -915,8 +915,8 @@ beatle J R Ringo False""")
self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '')
# Use 'format' to change the way a value is displayed.
- w = DateTimeInput(format='%d/%m/%Y %H:%M')
- self.assertHTMLEqual(w.render('date', d), '')
+ w = DateTimeInput(format='%d/%m/%Y %H:%M', attrs={'type': 'datetime'})
+ self.assertHTMLEqual(w.render('date', d), '')
self.assertFalse(w._has_changed(d, '17/09/2007 12:51'))
# Make sure a custom format works with _has_changed. The hidden input will use
@@ -938,8 +938,8 @@ beatle J R Ringo False""")
self.assertHTMLEqual(w.render('date', '2007-09-17'), '')
# Use 'format' to change the way a value is displayed.
- w = DateInput(format='%d/%m/%Y')
- self.assertHTMLEqual(w.render('date', d), '')
+ w = DateInput(format='%d/%m/%Y', attrs={'type': 'date'})
+ self.assertHTMLEqual(w.render('date', d), '')
self.assertFalse(w._has_changed(d, '17/09/2007'))
# Make sure a custom format works with _has_changed. The hidden input will use
@@ -963,8 +963,8 @@ beatle J R Ringo False""")
self.assertHTMLEqual(w.render('time', '13:12:11'), '')
# Use 'format' to change the way a value is displayed.
- w = TimeInput(format='%H:%M')
- self.assertHTMLEqual(w.render('time', t), '')
+ w = TimeInput(format='%H:%M', attrs={'type': 'time'})
+ self.assertHTMLEqual(w.render('time', t), '')
self.assertFalse(w._has_changed(t, '12:51'))
# Make sure a custom format works with _has_changed. The hidden input will use