Fixed #19686 -- Added HTML5 number input type

Thanks Simon Charette for his help on the patch. Refs #16630.
This commit is contained in:
Claude Paroz 2013-02-23 09:45:56 +01:00
parent dcf651c27e
commit 7ec2a21be1
12 changed files with 148 additions and 67 deletions

View File

@ -19,7 +19,7 @@ from django.core import validators
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.forms.util import ErrorList, from_current_timezone, to_current_timezone from django.forms.util import ErrorList, from_current_timezone, to_current_timezone
from django.forms.widgets import ( from django.forms.widgets import (
TextInput, PasswordInput, EmailInput, URLInput, HiddenInput, TextInput, NumberInput, EmailInput, URLInput, HiddenInput,
MultipleHiddenInput, ClearableFileInput, CheckboxInput, Select, MultipleHiddenInput, ClearableFileInput, CheckboxInput, Select,
NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput, NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput,
SplitDateTimeWidget, SplitHiddenDateTimeWidget, FILE_INPUT_CONTRADICTION SplitDateTimeWidget, SplitHiddenDateTimeWidget, FILE_INPUT_CONTRADICTION
@ -234,6 +234,7 @@ class IntegerField(Field):
def __init__(self, max_value=None, min_value=None, *args, **kwargs): def __init__(self, max_value=None, min_value=None, *args, **kwargs):
self.max_value, self.min_value = max_value, min_value self.max_value, self.min_value = max_value, min_value
kwargs.setdefault('widget', NumberInput if not kwargs.get('localize') else self.widget)
super(IntegerField, self).__init__(*args, **kwargs) super(IntegerField, self).__init__(*args, **kwargs)
if max_value is not None: if max_value is not None:
@ -257,6 +258,16 @@ class IntegerField(Field):
raise ValidationError(self.error_messages['invalid']) raise ValidationError(self.error_messages['invalid'])
return value return value
def widget_attrs(self, widget):
attrs = super(IntegerField, self).widget_attrs(widget)
if isinstance(widget, NumberInput):
if self.min_value is not None:
attrs['min'] = self.min_value
if self.max_value is not None:
attrs['max'] = self.max_value
return attrs
class FloatField(IntegerField): class FloatField(IntegerField):
default_error_messages = { default_error_messages = {
'invalid': _('Enter a number.'), 'invalid': _('Enter a number.'),
@ -278,25 +289,24 @@ class FloatField(IntegerField):
raise ValidationError(self.error_messages['invalid']) raise ValidationError(self.error_messages['invalid'])
return value return value
class DecimalField(Field): def widget_attrs(self, widget):
attrs = super(FloatField, self).widget_attrs(widget)
if isinstance(widget, NumberInput):
attrs.setdefault('step', 'any')
return attrs
class DecimalField(IntegerField):
default_error_messages = { default_error_messages = {
'invalid': _('Enter a number.'), 'invalid': _('Enter a number.'),
'max_value': _('Ensure this value is less than or equal to %(limit_value)s.'),
'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'),
'max_digits': _('Ensure that there are no more than %s digits in total.'), 'max_digits': _('Ensure that there are no more than %s digits in total.'),
'max_decimal_places': _('Ensure that there are no more than %s decimal places.'), 'max_decimal_places': _('Ensure that there are no more than %s decimal places.'),
'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.') 'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.')
} }
def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
self.max_value, self.min_value = max_value, min_value
self.max_digits, self.decimal_places = max_digits, decimal_places self.max_digits, self.decimal_places = max_digits, decimal_places
Field.__init__(self, *args, **kwargs) super(DecimalField, self).__init__(max_value, min_value, *args, **kwargs)
if max_value is not None:
self.validators.append(validators.MaxValueValidator(max_value))
if min_value is not None:
self.validators.append(validators.MinValueValidator(min_value))
def to_python(self, value): def to_python(self, value):
""" """
@ -345,6 +355,19 @@ class DecimalField(Field):
raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places)) raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
return value return value
def widget_attrs(self, widget):
attrs = super(DecimalField, self).widget_attrs(widget)
if isinstance(widget, NumberInput):
if self.max_digits is not None:
max_length = self.max_digits + 1 # for the sign
if self.decimal_places is None or self.decimal_places > 0:
max_length += 1 # for the dot
attrs['maxlength'] = max_length
if self.decimal_places:
attrs['step'] = '0.%s1' % ('0' * (self.decimal_places-1))
return attrs
class BaseTemporalField(Field): class BaseTemporalField(Field):
def __init__(self, input_formats=None, *args, **kwargs): def __init__(self, input_formats=None, *args, **kwargs):

View File

@ -23,7 +23,7 @@ from django.utils import datetime_safe, formats, six
__all__ = ( __all__ = (
'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'Media', 'MediaDefiningClass', 'Widget', 'TextInput',
'EmailInput', 'URLInput', 'PasswordInput', 'EmailInput', 'URLInput', 'NumberInput', 'PasswordInput',
'HiddenInput', 'MultipleHiddenInput', 'ClearableFileInput', 'HiddenInput', 'MultipleHiddenInput', 'ClearableFileInput',
'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput', 'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput',
'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
@ -252,6 +252,10 @@ class TextInput(Input):
super(TextInput, self).__init__(attrs) super(TextInput, self).__init__(attrs)
class NumberInput(TextInput):
input_type = 'number'
class EmailInput(TextInput): class EmailInput(TextInput):
input_type = 'email' input_type = 'email'

View File

@ -454,7 +454,8 @@ For each field, we describe the default widget used if you don't specify
.. class:: DecimalField(**kwargs) .. class:: DecimalField(**kwargs)
* Default widget: :class:`TextInput` * Default widget: :class:`NumberInput` when :attr:`Field.localize` is
``False``, else :class:`TextInput`.
* Empty value: ``None`` * Empty value: ``None``
* Normalizes to: A Python ``decimal``. * Normalizes to: A Python ``decimal``.
* Validates that the given value is a decimal. Leading and trailing * Validates that the given value is a decimal. Leading and trailing
@ -580,7 +581,8 @@ For each field, we describe the default widget used if you don't specify
.. class:: FloatField(**kwargs) .. class:: FloatField(**kwargs)
* Default widget: :class:`TextInput` * Default widget: :class:`NumberInput` when :attr:`Field.localize` is
``False``, else :class:`TextInput`.
* Empty value: ``None`` * Empty value: ``None``
* Normalizes to: A Python float. * Normalizes to: A Python float.
* Validates that the given value is an float. Leading and trailing * Validates that the given value is an float. Leading and trailing
@ -621,7 +623,8 @@ For each field, we describe the default widget used if you don't specify
.. class:: IntegerField(**kwargs) .. class:: IntegerField(**kwargs)
* Default widget: :class:`TextInput` * Default widget: :class:`NumberInput` when :attr:`Field.localize` is
``False``, else :class:`TextInput`.
* Empty value: ``None`` * Empty value: ``None``
* Normalizes to: A Python integer or long integer. * Normalizes to: A Python integer or long integer.
* Validates that the given value is an integer. Leading and trailing * Validates that the given value is an integer. Leading and trailing

View File

@ -389,6 +389,19 @@ These widgets make use of the HTML elements ``input`` and ``textarea``.
Text input: ``<input type="text" ...>`` Text input: ``<input type="text" ...>``
``NumberInput``
~~~~~~~~~~~~~~~
.. class:: NumberInput
.. versionadded:: 1.6
Text input: ``<input type="number" ...>``
Beware that not all browsers support entering localized numbers in
``number`` input types. Django itself avoids using them for fields having
their :attr:`~django.forms.Field.localize` property to ``True``.
``EmailInput`` ``EmailInput``
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~

View File

@ -60,9 +60,13 @@ Minor features
* In addition to :lookup:`year`, :lookup:`month` and :lookup:`day`, the ORM * In addition to :lookup:`year`, :lookup:`month` and :lookup:`day`, the ORM
now supports :lookup:`hour`, :lookup:`minute` and :lookup:`second` lookups. now supports :lookup:`hour`, :lookup:`minute` and :lookup:`second` lookups.
* The default widgets for :class:`~django.forms.EmailField` and * The default widgets for :class:`~django.forms.EmailField`,
:class:`~django.forms.URLField` use the new type attributes available in :class:`~django.forms.URLField`, :class:`~django.forms.IntegerField`,
HTML5 (type='email', type='url'). :class:`~django.forms.FloatField` and :class:`~django.forms.DecimalField` use
the new type attributes available in HTML5 (type='email', type='url',
type='number'). Note that due to erratic support of the ``number`` input type
with localized numbers in current browsers, Django only uses it when numeric
fields are not localized.
* The ``number`` argument for :ref:`lazy plural translations * The ``number`` argument for :ref:`lazy plural translations
<lazy-plural-translations>` can be provided at translation time rather than <lazy-plural-translations>` can be provided at translation time rather than
@ -122,7 +126,8 @@ Backwards incompatible changes in 1.6
* If your CSS/Javascript code used to access HTML input widgets by type, you * 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 should review it as ``type='text'`` widgets might be now output as
``type='email'`` or ``type='url'`` depending on their corresponding field type. ``type='email'``, ``type='url'`` or ``type='number'`` depending on their
corresponding field type.
* Extraction of translatable literals from templates with the * Extraction of translatable literals from templates with the
:djadmin:`makemessages` command now correctly detects i18n constructs when :djadmin:`makemessages` command now correctly detects i18n constructs when

View File

@ -273,13 +273,13 @@ Lets you create a formset with the ability to order::
... print(form.as_table()) ... print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr>
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr>
<tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="text" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr> <tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="number" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr>
<tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr>
<tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr>
<tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="text" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr> <tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="number" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr>
<tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr>
<tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr>
<tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="text" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr> <tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="number" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr>
This adds an additional field to each form. This new field is named ``ORDER`` This adds an additional field to each form. This new field is named ``ORDER``
and is an ``forms.IntegerField``. For the forms that came from the initial and is an ``forms.IntegerField``. For the forms that came from the initial

View File

@ -1133,7 +1133,7 @@ class OldFormForXTests(TestCase):
<option value="%s">Joe Better</option> <option value="%s">Joe Better</option>
<option value="%s">Mike Royko</option> <option value="%s">Mike Royko</option>
</select></p> </select></p>
<p><label for="id_age">Age:</label> <input type="text" name="age" id="id_age" /></p>''' % (w_woodward.pk, w_bernstein.pk, bw.pk, w_royko.pk)) <p><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" min="0" /></p>''' % (w_woodward.pk, w_bernstein.pk, bw.pk, w_royko.pk))
data = { data = {
'writer': six.text_type(w_woodward.pk), 'writer': six.text_type(w_woodward.pk),
@ -1151,7 +1151,7 @@ class OldFormForXTests(TestCase):
<option value="%s">Joe Better</option> <option value="%s">Joe Better</option>
<option value="%s">Mike Royko</option> <option value="%s">Mike Royko</option>
</select></p> </select></p>
<p><label for="id_age">Age:</label> <input type="text" name="age" value="65" id="id_age" /></p>''' % (w_woodward.pk, w_bernstein.pk, bw.pk, w_royko.pk)) <p><label for="id_age">Age:</label> <input type="number" name="age" value="65" id="id_age" min="0" /></p>''' % (w_woodward.pk, w_bernstein.pk, bw.pk, w_royko.pk))
def test_file_field(self): def test_file_field(self):
# Test conditions when files is either not given or empty. # Test conditions when files is either not given or empty.

View File

@ -392,7 +392,7 @@ class ModelFormsetTest(TestCase):
self.assertEqual(len(formset.forms), 1) self.assertEqual(len(formset.forms), 1)
self.assertHTMLEqual(formset.forms[0].as_p(), self.assertHTMLEqual(formset.forms[0].as_p(),
'<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></p>\n' '<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></p>\n'
'<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" id="id_form-0-author_ptr" /></p>') '<p><label for="id_form-0-write_speed">Write speed:</label> <input type="number" name="form-0-write_speed" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" id="id_form-0-author_ptr" /></p>')
data = { data = {
'form-TOTAL_FORMS': '1', # the number of forms rendered 'form-TOTAL_FORMS': '1', # the number of forms rendered
@ -415,10 +415,10 @@ class ModelFormsetTest(TestCase):
self.assertEqual(len(formset.forms), 2) self.assertEqual(len(formset.forms), 2)
self.assertHTMLEqual(formset.forms[0].as_p(), self.assertHTMLEqual(formset.forms[0].as_p(),
'<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Ernest Hemingway" maxlength="100" /></p>\n' '<p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Ernest Hemingway" maxlength="100" /></p>\n'
'<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" value="%d" id="id_form-0-author_ptr" /></p>' % hemingway_id) '<p><label for="id_form-0-write_speed">Write speed:</label> <input type="number" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" value="%d" id="id_form-0-author_ptr" /></p>' % hemingway_id)
self.assertHTMLEqual(formset.forms[1].as_p(), self.assertHTMLEqual(formset.forms[1].as_p(),
'<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /></p>\n' '<p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /></p>\n'
'<p><label for="id_form-1-write_speed">Write speed:</label> <input type="text" name="form-1-write_speed" id="id_form-1-write_speed" /><input type="hidden" name="form-1-author_ptr" id="id_form-1-author_ptr" /></p>') '<p><label for="id_form-1-write_speed">Write speed:</label> <input type="number" name="form-1-write_speed" id="id_form-1-write_speed" /><input type="hidden" name="form-1-author_ptr" id="id_form-1-author_ptr" /></p>')
data = { data = {
'form-TOTAL_FORMS': '2', # the number of forms rendered 'form-TOTAL_FORMS': '2', # the number of forms rendered
@ -551,6 +551,7 @@ class ModelFormsetTest(TestCase):
def test_inline_formsets_with_custom_pk(self): def test_inline_formsets_with_custom_pk(self):
# Test inline formsets where the inline-edited object has a custom # Test inline formsets where the inline-edited object has a custom
# primary key that is not the fk to the parent object. # primary key that is not the fk to the parent object.
self.maxDiff = 1024
AuthorBooksFormSet2 = inlineformset_factory(Author, BookWithCustomPK, can_delete=False, extra=1) AuthorBooksFormSet2 = inlineformset_factory(Author, BookWithCustomPK, can_delete=False, extra=1)
author = Author.objects.create(pk=1, name='Charles Baudelaire') author = Author.objects.create(pk=1, name='Charles Baudelaire')
@ -558,7 +559,7 @@ class ModelFormsetTest(TestCase):
formset = AuthorBooksFormSet2(instance=author) formset = AuthorBooksFormSet2(instance=author)
self.assertEqual(len(formset.forms), 1) self.assertEqual(len(formset.forms), 1)
self.assertHTMLEqual(formset.forms[0].as_p(), self.assertHTMLEqual(formset.forms[0].as_p(),
'<p><label for="id_bookwithcustompk_set-0-my_pk">My pk:</label> <input type="text" name="bookwithcustompk_set-0-my_pk" id="id_bookwithcustompk_set-0-my_pk" /></p>\n' '<p><label for="id_bookwithcustompk_set-0-my_pk">My pk:</label> <input id="id_bookwithcustompk_set-0-my_pk" type="number" name="bookwithcustompk_set-0-my_pk" maxlength="6" /></p>\n'
'<p><label for="id_bookwithcustompk_set-0-title">Title:</label> <input id="id_bookwithcustompk_set-0-title" type="text" name="bookwithcustompk_set-0-title" maxlength="100" /><input type="hidden" name="bookwithcustompk_set-0-author" value="1" id="id_bookwithcustompk_set-0-author" /></p>') '<p><label for="id_bookwithcustompk_set-0-title">Title:</label> <input id="id_bookwithcustompk_set-0-title" type="text" name="bookwithcustompk_set-0-title" maxlength="100" /><input type="hidden" name="bookwithcustompk_set-0-author" value="1" id="id_bookwithcustompk_set-0-author" /></p>')
data = { data = {
@ -806,7 +807,7 @@ class ModelFormsetTest(TestCase):
'<option value="%d">Joe Perry at Giordanos</option>\n' '<option value="%d">Joe Perry at Giordanos</option>\n'
'<option value="%d">Jack Berry at Giordanos</option>\n' '<option value="%d">Jack Berry at Giordanos</option>\n'
'</select></p>\n' '</select></p>\n'
'<p><label for="id_form-0-age">Age:</label> <input type="text" name="form-0-age" id="id_form-0-age" /></p>' '<p><label for="id_form-0-age">Age:</label> <input type="number" name="form-0-age" id="id_form-0-age" min="0" /></p>'
% (owner1.auto_id, owner2.auto_id)) % (owner1.auto_id, owner2.auto_id))
owner1 = Owner.objects.get(name='Joe Perry') owner1 = Owner.objects.get(name='Joe Perry')
@ -816,7 +817,7 @@ class ModelFormsetTest(TestCase):
formset = FormSet(instance=owner1) formset = FormSet(instance=owner1)
self.assertEqual(len(formset.forms), 1) self.assertEqual(len(formset.forms), 1)
self.assertHTMLEqual(formset.forms[0].as_p(), self.assertHTMLEqual(formset.forms[0].as_p(),
'<p><label for="id_ownerprofile-0-age">Age:</label> <input type="text" name="ownerprofile-0-age" id="id_ownerprofile-0-age" /><input type="hidden" name="ownerprofile-0-owner" value="%d" id="id_ownerprofile-0-owner" /></p>' '<p><label for="id_ownerprofile-0-age">Age:</label> <input type="number" name="ownerprofile-0-age" id="id_ownerprofile-0-age" min="0" /><input type="hidden" name="ownerprofile-0-owner" value="%d" id="id_ownerprofile-0-owner" /></p>'
% owner1.auto_id) % owner1.auto_id)
data = { data = {
@ -837,7 +838,7 @@ class ModelFormsetTest(TestCase):
formset = FormSet(instance=owner1) formset = FormSet(instance=owner1)
self.assertEqual(len(formset.forms), 1) self.assertEqual(len(formset.forms), 1)
self.assertHTMLEqual(formset.forms[0].as_p(), self.assertHTMLEqual(formset.forms[0].as_p(),
'<p><label for="id_ownerprofile-0-age">Age:</label> <input type="text" name="ownerprofile-0-age" value="54" id="id_ownerprofile-0-age" /><input type="hidden" name="ownerprofile-0-owner" value="%d" id="id_ownerprofile-0-owner" /></p>' '<p><label for="id_ownerprofile-0-age">Age:</label> <input type="number" name="ownerprofile-0-age" value="54" id="id_ownerprofile-0-age" min="0" /><input type="hidden" name="ownerprofile-0-owner" value="%d" id="id_ownerprofile-0-owner" /></p>'
% owner1.auto_id) % owner1.auto_id)
data = { data = {
@ -993,7 +994,7 @@ class ModelFormsetTest(TestCase):
result = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?', '__DATETIME__', result) result = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?', '__DATETIME__', result)
self.assertHTMLEqual(result, self.assertHTMLEqual(result,
'<p><label for="id_membership_set-0-date_joined">Date joined:</label> <input type="text" name="membership_set-0-date_joined" value="__DATETIME__" id="id_membership_set-0-date_joined" /><input type="hidden" name="initial-membership_set-0-date_joined" value="__DATETIME__" id="initial-membership_set-0-id_membership_set-0-date_joined" /></p>\n' '<p><label for="id_membership_set-0-date_joined">Date joined:</label> <input type="text" name="membership_set-0-date_joined" value="__DATETIME__" id="id_membership_set-0-date_joined" /><input type="hidden" name="initial-membership_set-0-date_joined" value="__DATETIME__" id="initial-membership_set-0-id_membership_set-0-date_joined" /></p>\n'
'<p><label for="id_membership_set-0-karma">Karma:</label> <input type="text" name="membership_set-0-karma" id="id_membership_set-0-karma" /><input type="hidden" name="membership_set-0-person" value="%d" id="id_membership_set-0-person" /><input type="hidden" name="membership_set-0-id" id="id_membership_set-0-id" /></p>' '<p><label for="id_membership_set-0-karma">Karma:</label> <input type="number" name="membership_set-0-karma" id="id_membership_set-0-karma" /><input type="hidden" name="membership_set-0-person" value="%d" id="id_membership_set-0-person" /><input type="hidden" name="membership_set-0-id" id="id_membership_set-0-id" /></p>'
% person.id) % person.id)
# test for validation with callable defaults. Validations rely on hidden fields # test for validation with callable defaults. Validations rely on hidden fields

View File

@ -35,7 +35,6 @@ from decimal import Decimal
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.forms import * from django.forms import *
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.utils import formats
from django.utils import six from django.utils import six
from django.utils._os import upath from django.utils._os import upath
@ -131,6 +130,7 @@ class FieldsTests(SimpleTestCase):
def test_integerfield_1(self): def test_integerfield_1(self):
f = IntegerField() f = IntegerField()
self.assertWidgetRendersTo(f, '<input type="number" name="f" id="id_f" />')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertEqual(1, f.clean('1')) self.assertEqual(1, f.clean('1'))
@ -165,6 +165,7 @@ class FieldsTests(SimpleTestCase):
def test_integerfield_3(self): def test_integerfield_3(self):
f = IntegerField(max_value=10) f = IntegerField(max_value=10)
self.assertWidgetRendersTo(f, '<input max="10" type="number" name="f" id="id_f" />')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertEqual(1, f.clean(1)) self.assertEqual(1, f.clean(1))
self.assertEqual(10, f.clean(10)) self.assertEqual(10, f.clean(10))
@ -176,6 +177,7 @@ class FieldsTests(SimpleTestCase):
def test_integerfield_4(self): def test_integerfield_4(self):
f = IntegerField(min_value=10) f = IntegerField(min_value=10)
self.assertWidgetRendersTo(f, '<input id="id_f" type="number" name="f" min="10" />')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'", f.clean, 1) self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'", f.clean, 1)
self.assertEqual(10, f.clean(10)) self.assertEqual(10, f.clean(10))
@ -187,6 +189,7 @@ class FieldsTests(SimpleTestCase):
def test_integerfield_5(self): def test_integerfield_5(self):
f = IntegerField(min_value=10, max_value=20) f = IntegerField(min_value=10, max_value=20)
self.assertWidgetRendersTo(f, '<input id="id_f" max="20" type="number" name="f" min="10" />')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'", f.clean, 1) self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'", f.clean, 1)
self.assertEqual(10, f.clean(10)) self.assertEqual(10, f.clean(10))
@ -198,10 +201,19 @@ class FieldsTests(SimpleTestCase):
self.assertEqual(f.max_value, 20) self.assertEqual(f.max_value, 20)
self.assertEqual(f.min_value, 10) self.assertEqual(f.min_value, 10)
def test_integerfield_localized(self):
"""
Make sure localized IntegerField's widget renders to a text input with
no number input specific attributes.
"""
f1 = IntegerField(localize=True)
self.assertWidgetRendersTo(f1, '<input id="id_f" name="f" type="text" />')
# FloatField ################################################################## # FloatField ##################################################################
def test_floatfield_1(self): def test_floatfield_1(self):
f = FloatField() f = FloatField()
self.assertWidgetRendersTo(f, '<input step="any" type="number" name="f" id="id_f" />')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertEqual(1.0, f.clean('1')) self.assertEqual(1.0, f.clean('1'))
@ -228,6 +240,7 @@ class FieldsTests(SimpleTestCase):
def test_floatfield_3(self): def test_floatfield_3(self):
f = FloatField(max_value=1.5, min_value=0.5) f = FloatField(max_value=1.5, min_value=0.5)
self.assertWidgetRendersTo(f, '<input step="any" name="f" min="0.5" max="1.5" type="number" id="id_f" />')
self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6') self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6')
self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4') self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4')
self.assertEqual(1.5, f.clean('1.5')) self.assertEqual(1.5, f.clean('1.5'))
@ -235,10 +248,19 @@ class FieldsTests(SimpleTestCase):
self.assertEqual(f.max_value, 1.5) self.assertEqual(f.max_value, 1.5)
self.assertEqual(f.min_value, 0.5) self.assertEqual(f.min_value, 0.5)
def test_floatfield_localized(self):
"""
Make sure localized FloatField's widget renders to a text input with
no number input specific attributes.
"""
f = FloatField(localize=True)
self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" />')
# DecimalField ################################################################ # DecimalField ################################################################
def test_decimalfield_1(self): def test_decimalfield_1(self):
f = DecimalField(max_digits=4, decimal_places=2) f = DecimalField(max_digits=4, decimal_places=2)
self.assertWidgetRendersTo(f, '<input id="id_f" step="0.01" type="number" name="f" maxlength="6" />')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertEqual(f.clean('1'), Decimal("1")) self.assertEqual(f.clean('1'), Decimal("1"))
@ -284,6 +306,7 @@ class FieldsTests(SimpleTestCase):
def test_decimalfield_3(self): def test_decimalfield_3(self):
f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5')) f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5'))
self.assertWidgetRendersTo(f, '<input step="0.01" name="f" min="0.5" max="1.5" maxlength="6" type="number" id="id_f" />')
self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6') self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6')
self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4') self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4')
self.assertEqual(f.clean('1.5'), Decimal("1.5")) self.assertEqual(f.clean('1.5'), Decimal("1.5"))
@ -315,6 +338,14 @@ class FieldsTests(SimpleTestCase):
self.assertEqual(f.clean('.01'), Decimal(".01")) self.assertEqual(f.clean('.01'), Decimal(".01"))
self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 0 digits before the decimal point.'", f.clean, '1.1') self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 0 digits before the decimal point.'", f.clean, '1.1')
def test_decimalfield_localized(self):
"""
Make sure localized DecimalField's widget renders to a text input with
no number input specific attributes.
"""
f = DecimalField(localize=True)
self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" />')
# DateField ################################################################### # DateField ###################################################################
def test_datefield_1(self): def test_datefield_1(self):

View File

@ -1740,7 +1740,7 @@ class FormsTestCase(TestCase):
<option value="3">No</option> <option value="3">No</option>
</select></li> </select></li>
<li><label for="id_email">Email:</label> <input type="email" 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>""") <li class="required error"><ul class="errorlist"><li>This field is required.</li></ul><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" /></li>""")
self.assertHTMLEqual(p.as_p(), """<ul class="errorlist"><li>This field is required.</li></ul> self.assertHTMLEqual(p.as_p(), """<ul class="errorlist"><li>This field is required.</li></ul>
<p class="required error"><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p> <p class="required error"><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p>
@ -1751,7 +1751,7 @@ class FormsTestCase(TestCase):
</select></p> </select></p>
<p><label for="id_email">Email:</label> <input type="email" 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> <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>""") <p class="required error"><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" /></p>""")
self.assertHTMLEqual(p.as_table(), """<tr class="required error"><th><label for="id_name">Name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="name" id="id_name" /></td></tr> self.assertHTMLEqual(p.as_table(), """<tr class="required error"><th><label for="id_name">Name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="name" id="id_name" /></td></tr>
<tr class="required"><th><label for="id_is_cool">Is cool:</label></th><td><select name="is_cool" id="id_is_cool"> <tr class="required"><th><label for="id_is_cool">Is cool:</label></th><td><select name="is_cool" id="id_is_cool">
@ -1760,7 +1760,7 @@ class FormsTestCase(TestCase):
<option value="3">No</option> <option value="3">No</option>
</select></td></tr> </select></td></tr>
<tr><th><label for="id_email">Email:</label></th><td><input type="email" 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>""") <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="number" name="age" id="id_age" /></td></tr>""")
def test_label_split_datetime_not_displayed(self): def test_label_split_datetime_not_displayed(self):
class EventForm(Form): class EventForm(Form):

View File

@ -53,7 +53,7 @@ class FormsFormsetTestCase(TestCase):
formset = ChoiceFormSet(auto_id=False, prefix='choices') formset = ChoiceFormSet(auto_id=False, prefix='choices')
self.assertHTMLEqual(str(formset), """<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="1000" /> self.assertHTMLEqual(str(formset), """<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="1000" />
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr> <tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr>
<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr>""") <tr><th>Votes:</th><td><input type="number" name="choices-0-votes" /></td></tr>""")
# On thing to note is that there needs to be a special value in the data. This # On thing to note is that there needs to be a special value in the data. This
# value tells the FormSet how many forms were displayed so it can tell how # value tells the FormSet how many forms were displayed so it can tell how
@ -137,9 +137,9 @@ class FormsFormsetTestCase(TestCase):
form_output.append(form.as_ul()) form_output.append(form.as_ul())
self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
<li>Choice: <input type="text" name="choices-1-choice" /></li> <li>Choice: <input type="text" name="choices-1-choice" /></li>
<li>Votes: <input type="text" name="choices-1-votes" /></li>""") <li>Votes: <input type="number" name="choices-1-votes" /></li>""")
# Let's simulate what would happen if we submitted this form. # Let's simulate what would happen if we submitted this form.
@ -210,11 +210,11 @@ class FormsFormsetTestCase(TestCase):
form_output.append(form.as_ul()) form_output.append(form.as_ul())
self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" /></li> self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" /></li>
<li>Votes: <input type="text" name="choices-0-votes" /></li> <li>Votes: <input type="number" name="choices-0-votes" /></li>
<li>Choice: <input type="text" name="choices-1-choice" /></li> <li>Choice: <input type="text" name="choices-1-choice" /></li>
<li>Votes: <input type="text" name="choices-1-votes" /></li> <li>Votes: <input type="number" name="choices-1-votes" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li> <li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li>""") <li>Votes: <input type="number" name="choices-2-votes" /></li>""")
# Since we displayed every form as blank, we will also accept them back as blank. # Since we displayed every form as blank, we will also accept them back as blank.
# This may seem a little strange, but later we will show how to require a minimum # This may seem a little strange, but later we will show how to require a minimum
@ -301,19 +301,19 @@ class FormsFormsetTestCase(TestCase):
form_output.append(form.as_ul()) form_output.append(form.as_ul())
self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
<li>Choice: <input type="text" name="choices-1-choice" /></li> <li>Choice: <input type="text" name="choices-1-choice" /></li>
<li>Votes: <input type="text" name="choices-1-votes" /></li> <li>Votes: <input type="number" name="choices-1-votes" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li> <li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li> <li>Votes: <input type="number" name="choices-2-votes" /></li>
<li>Choice: <input type="text" name="choices-3-choice" /></li> <li>Choice: <input type="text" name="choices-3-choice" /></li>
<li>Votes: <input type="text" name="choices-3-votes" /></li>""") <li>Votes: <input type="number" name="choices-3-votes" /></li>""")
# Make sure retrieving an empty form works, and it shows up in the form list # Make sure retrieving an empty form works, and it shows up in the form list
self.assertTrue(formset.empty_form.empty_permitted) self.assertTrue(formset.empty_form.empty_permitted)
self.assertHTMLEqual(formset.empty_form.as_ul(), """<li>Choice: <input type="text" name="choices-__prefix__-choice" /></li> self.assertHTMLEqual(formset.empty_form.as_ul(), """<li>Choice: <input type="text" name="choices-__prefix__-choice" /></li>
<li>Votes: <input type="text" name="choices-__prefix__-votes" /></li>""") <li>Votes: <input type="number" name="choices-__prefix__-votes" /></li>""")
def test_formset_with_deletion(self): def test_formset_with_deletion(self):
# FormSets with deletion ###################################################### # FormSets with deletion ######################################################
@ -331,13 +331,13 @@ class FormsFormsetTestCase(TestCase):
form_output.append(form.as_ul()) form_output.append(form.as_ul())
self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li> <li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li> <li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li> <li>Votes: <input type="number" name="choices-1-votes" value="900" /></li>
<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li> <li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li> <li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li> <li>Votes: <input type="number" name="choices-2-votes" /></li>
<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>""") <li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>""")
# To delete something, we just need to set that form's special delete field to # To delete something, we just need to set that form's special delete field to
@ -428,14 +428,14 @@ class FormsFormsetTestCase(TestCase):
form_output.append(form.as_ul()) form_output.append(form.as_ul())
self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
<li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li> <li>Order: <input type="number" name="choices-0-ORDER" value="1" /></li>
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li> <li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li> <li>Votes: <input type="number" name="choices-1-votes" value="900" /></li>
<li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li> <li>Order: <input type="number" name="choices-1-ORDER" value="2" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li> <li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li> <li>Votes: <input type="number" name="choices-2-votes" /></li>
<li>Order: <input type="text" name="choices-2-ORDER" /></li>""") <li>Order: <input type="number" name="choices-2-ORDER" /></li>""")
data = { data = {
'choices-TOTAL_FORMS': '3', # the number of forms rendered 'choices-TOTAL_FORMS': '3', # the number of forms rendered
@ -539,20 +539,20 @@ class FormsFormsetTestCase(TestCase):
form_output.append(form.as_ul()) form_output.append(form.as_ul())
self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
<li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li> <li>Order: <input type="number" name="choices-0-ORDER" value="1" /></li>
<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li> <li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li> <li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li> <li>Votes: <input type="number" name="choices-1-votes" value="900" /></li>
<li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li> <li>Order: <input type="number" name="choices-1-ORDER" value="2" /></li>
<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li> <li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
<li>Choice: <input type="text" name="choices-2-choice" value="The Decemberists" /></li> <li>Choice: <input type="text" name="choices-2-choice" value="The Decemberists" /></li>
<li>Votes: <input type="text" name="choices-2-votes" value="500" /></li> <li>Votes: <input type="number" name="choices-2-votes" value="500" /></li>
<li>Order: <input type="text" name="choices-2-ORDER" value="3" /></li> <li>Order: <input type="number" name="choices-2-ORDER" value="3" /></li>
<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li> <li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>
<li>Choice: <input type="text" name="choices-3-choice" /></li> <li>Choice: <input type="text" name="choices-3-choice" /></li>
<li>Votes: <input type="text" name="choices-3-votes" /></li> <li>Votes: <input type="number" name="choices-3-votes" /></li>
<li>Order: <input type="text" name="choices-3-ORDER" /></li> <li>Order: <input type="number" name="choices-3-ORDER" /></li>
<li>Delete: <input type="checkbox" name="choices-3-DELETE" /></li>""") <li>Delete: <input type="checkbox" name="choices-3-DELETE" /></li>""")
# Let's delete Fergie, and put The Decemberists ahead of Calexico. # Let's delete Fergie, and put The Decemberists ahead of Calexico.
@ -956,19 +956,19 @@ class FormsetAsFooTests(TestCase):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices') formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertHTMLEqual(formset.as_table(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" /> self.assertHTMLEqual(formset.as_table(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" value="Calexico" /></td></tr> <tr><th>Choice:</th><td><input type="text" name="choices-0-choice" value="Calexico" /></td></tr>
<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" value="100" /></td></tr>""") <tr><th>Votes:</th><td><input type="number" name="choices-0-votes" value="100" /></td></tr>""")
def test_as_p(self): def test_as_p(self):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices') formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertHTMLEqual(formset.as_p(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" /> self.assertHTMLEqual(formset.as_p(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
<p>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></p> <p>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></p>
<p>Votes: <input type="text" name="choices-0-votes" value="100" /></p>""") <p>Votes: <input type="number" name="choices-0-votes" value="100" /></p>""")
def test_as_ul(self): def test_as_ul(self):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices') formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertHTMLEqual(formset.as_ul(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" /> self.assertHTMLEqual(formset.as_ul(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>""") <li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>""")
# Regression test for #11418 ################################################# # Regression test for #11418 #################################################

View File

@ -651,6 +651,7 @@ class FormattingTests(TestCase):
""" """
Tests if form input is correctly localized Tests if form input is correctly localized
""" """
self.maxDiff = 1200
with translation.override('de-at', deactivate=True): with translation.override('de-at', deactivate=True):
form6 = CompanyForm({ form6 = CompanyForm({
'name': 'acme', 'name': 'acme',