Permalink
Browse files

Added HTML5 email input type

Refs #16630.
  • Loading branch information...
claudep committed Jan 28, 2013
1 parent c47fa3b commit 4f16376274a4e52074722c615fccef5fac5f009a
View
@@ -18,10 +18,12 @@
from django.core import validators
from django.core.exceptions import ValidationError
from django.forms.util import ErrorList, from_current_timezone, to_current_timezone
-from django.forms.widgets import (TextInput, PasswordInput, HiddenInput,
+from django.forms.widgets import (
+ TextInput, PasswordInput, EmailInput, HiddenInput,
MultipleHiddenInput, ClearableFileInput, CheckboxInput, Select,
NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput,
- SplitDateTimeWidget, SplitHiddenDateTimeWidget, FILE_INPUT_CONTRADICTION)
+ SplitDateTimeWidget, SplitHiddenDateTimeWidget, FILE_INPUT_CONTRADICTION
+)
from django.utils import formats
from django.utils.encoding import smart_text, force_str, force_text
from django.utils.ipv6 import clean_ipv6_address
@@ -487,6 +489,7 @@ def _set_regex(self, regex):
regex = property(_get_regex, _set_regex)
class EmailField(CharField):
+ widget = EmailInput
default_error_messages = {
'invalid': _('Enter a valid email address.'),
}
View
@@ -22,7 +22,7 @@
from django.utils import datetime_safe, formats, six
__all__ = (
- 'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput',
+ 'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'EmailInput', 'PasswordInput',
'HiddenInput', 'MultipleHiddenInput', 'ClearableFileInput',
'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput',
'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
@@ -251,6 +251,10 @@ def __init__(self, attrs=None):
super(TextInput, self).__init__(attrs)
+class EmailInput(TextInput):
+ input_type = 'email'
+
+
class PasswordInput(TextInput):
input_type = 'password'
View

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -212,17 +212,17 @@ fields. We've specified ``auto_id=False`` to simplify the output::
>>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br /><span class="helptext">100 characters max.</span></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
- <tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid email address, please.</td></tr>
+ <tr><th>Sender:</th><td><input type="email" name="sender" /><br />A valid email address, please.</td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul()))
<li>Subject: <input type="text" name="subject" maxlength="100" /> <span class="helptext">100 characters max.</span></li>
<li>Message: <input type="text" name="message" /></li>
- <li>Sender: <input type="text" name="sender" /> A valid email address, please.</li>
+ <li>Sender: <input type="email" name="sender" /> A valid email address, please.</li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" /> <span class="helptext">100 characters max.</span></p>
<p>Message: <input type="text" name="message" /></p>
- <p>Sender: <input type="text" name="sender" /> A valid email address, please.</p>
+ <p>Sender: <input type="email" name="sender" /> A valid email address, please.</p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
``error_messages``
@@ -489,7 +489,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: EmailField(**kwargs)
- * Default widget: :class:`TextInput`
+ * Default widget: :class:`EmailInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object.
* Validates that the given value is a valid email address, using a
View
@@ -392,7 +392,16 @@ These widgets make use of the HTML elements ``input`` and ``textarea``.
.. class:: TextInput
- Text input: ``<input type='text' ...>``
+ Text input: ``<input type="text" ...>``
+
+``EmailInput``
+~~~~~~~~~~~~~~
+
+.. class:: EmailInput
+
+ .. versionadded:: 1.6
+
+ Text input: ``<input type="email" ...>``
``PasswordInput``
~~~~~~~~~~~~~~~~~
View
@@ -31,6 +31,9 @@ Minor features
* Added :meth:`~django.db.models.query.QuerySet.earliest` for symmetry with
:meth:`~django.db.models.query.QuerySet.latest`.
+* The default widgets for :class:`~django.forms.EmailField` use
+ the new type attribute available in HTML5 (type='email').
+
Backwards incompatible changes in 1.6
=====================================
@@ -39,6 +42,10 @@ Backwards incompatible changes in 1.6
:meth:`~django.db.models.query.QuerySet.none` has been called:
``isinstance(qs.none(), EmptyQuerySet)``
+* If your CSS/Javascript code used to access HTML input widgets by type, you
+ should review it as ``type='text'`` widgets might be now output as
+ ``type='email'`` depending on their corresponding field type.
+
.. warning::
In addition to the changes outlined in this section, be sure to review the
@@ -223,7 +223,7 @@ wrapped in a paragraph. Here's the output for our example template::
<p><label for="id_message">Message:</label>
<input type="text" name="message" id="id_message" /></p>
<p><label for="id_sender">Sender:</label>
- <input type="text" name="sender" id="id_sender" /></p>
+ <input type="email" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
<input type="submit" value="Submit" />
@@ -631,7 +631,7 @@ class CommentForm(Form):
f = CommentForm(data, auto_id=False, error_class=DivErrorList)
self.assertHTMLEqual(f.as_p(), """<p>Name: <input type="text" name="name" maxlength="50" /></p>
<div class="errorlist"><div class="error">Enter a valid email address.</div></div>
-<p>Email: <input type="text" name="email" value="invalid" /></p>
+<p>Email: <input type="email" name="email" value="invalid" /></p>
<div class="errorlist"><div class="error">This field is required.</div></div>
<p>Comment: <input type="text" name="comment" /></p>""")
@@ -53,6 +53,11 @@ def fix_os_paths(x):
class FieldsTests(SimpleTestCase):
+ def assertWidgetRendersTo(self, field, to):
+ class _Form(Form):
+ f = field
+ self.assertHTMLEqual(str(_Form()['f']), to)
+
def test_field_sets_widget_is_required(self):
self.assertTrue(Field(required=True).widget.is_required)
self.assertFalse(Field(required=False).widget.is_required)
@@ -545,6 +550,7 @@ def test_change_regex_after_init(self):
def test_emailfield_1(self):
f = EmailField()
+ self.assertWidgetRendersTo(f, '<input type="email" name="f" id="id_f" />')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertEqual('person@example.com', f.clean('person@example.com'))
@@ -569,6 +575,7 @@ def test_emailfield_not_required(self):
def test_emailfield_min_max_length(self):
f = EmailField(min_length=10, max_length=15)
+ self.assertWidgetRendersTo(f, '<input id="id_f" type="email" name="f" maxlength="15" />')
self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 10 characters (it has 9).'", f.clean, 'a@foo.com')
self.assertEqual('alf@foo.com', f.clean('alf@foo.com'))
self.assertRaisesMessage(ValidationError, "'Ensure this value has at most 15 characters (it has 20).'", f.clean, 'alf123456788@foo.com')
@@ -245,11 +245,11 @@ class SignupForm(Form):
get_spam = BooleanField()
f = SignupForm(auto_id=False)
- self.assertHTMLEqual(str(f['email']), '<input type="text" name="email" />')
+ self.assertHTMLEqual(str(f['email']), '<input type="email" name="email" />')
self.assertHTMLEqual(str(f['get_spam']), '<input type="checkbox" name="get_spam" />')
f = SignupForm({'email': 'test@example.com', 'get_spam': True}, auto_id=False)
- self.assertHTMLEqual(str(f['email']), '<input type="text" name="email" value="test@example.com" />')
+ self.assertHTMLEqual(str(f['email']), '<input type="email" name="email" value="test@example.com" />')
self.assertHTMLEqual(str(f['get_spam']), '<input checked="checked" type="checkbox" name="get_spam" />')
# 'True' or 'true' should be rendered without a value attribute
@@ -1739,7 +1739,7 @@ class Person(Form):
<option value="2">Yes</option>
<option value="3">No</option>
</select></li>
-<li><label for="id_email">Email:</label> <input type="text" name="email" id="id_email" /></li>
+<li><label for="id_email">Email:</label> <input type="email" name="email" id="id_email" /></li>
<li class="required error"><ul class="errorlist"><li>This field is required.</li></ul><label for="id_age">Age:</label> <input type="text" name="age" id="id_age" /></li>""")
self.assertHTMLEqual(p.as_p(), """<ul class="errorlist"><li>This field is required.</li></ul>
@@ -1749,7 +1749,7 @@ class Person(Form):
<option value="2">Yes</option>
<option value="3">No</option>
</select></p>
-<p><label for="id_email">Email:</label> <input type="text" name="email" id="id_email" /></p>
+<p><label for="id_email">Email:</label> <input type="email" name="email" id="id_email" /></p>
<ul class="errorlist"><li>This field is required.</li></ul>
<p class="required error"><label for="id_age">Age:</label> <input type="text" name="age" id="id_age" /></p>""")
@@ -1759,7 +1759,7 @@ class Person(Form):
<option value="2">Yes</option>
<option value="3">No</option>
</select></td></tr>
-<tr><th><label for="id_email">Email:</label></th><td><input type="text" name="email" id="id_email" /></td></tr>
+<tr><th><label for="id_email">Email:</label></th><td><input type="email" name="email" id="id_email" /></td></tr>
<tr class="required error"><th><label for="id_age">Age:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="age" id="id_age" /></td></tr>""")
def test_label_split_datetime_not_displayed(self):

0 comments on commit 4f16376

Please sign in to comment.