diff --git a/django/forms/fields.py b/django/forms/fields.py index 7f0d26d1aa7..124e4f669ad 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 7651efccd07..763da0cff22 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 eab314a4cdd..1935cb23bc1 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 544ca416429..104144b2881 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