Ticket #16630: html5-input-types-take4.patch

File html5-input-types-take4.patch, 52.8 KB (added by Claude Paroz, 12 years ago)

Patch, tests, docs

  • django/forms/fields.py

    diff --git a/django/forms/fields.py b/django/forms/fields.py
    index 124e4f6..8dea508 100644
    a b from io import BytesIO  
    1818from django.core import validators
    1919from django.core.exceptions import ValidationError
    2020from django.forms.util import ErrorList, from_current_timezone, to_current_timezone
    21 from django.forms.widgets import (TextInput, PasswordInput, HiddenInput,
     21from django.forms.widgets import (
     22    TextInput, NumberInput, PasswordInput, EmailInput, URLInput, HiddenInput,
    2223    MultipleHiddenInput, ClearableFileInput, CheckboxInput, Select,
    2324    NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput,
    24     SplitDateTimeWidget, SplitHiddenDateTimeWidget, FILE_INPUT_CONTRADICTION)
     25    SplitDateTimeWidget, SplitHiddenDateTimeWidget, FILE_INPUT_CONTRADICTION
     26)
    2527from django.utils import formats
    2628from django.utils.encoding import smart_text, force_text
    2729from django.utils.ipv6 import clean_ipv6_address
    class CharField(Field):  
    205207        return attrs
    206208
    207209class IntegerField(Field):
     210    widget = NumberInput
    208211    default_error_messages = {
    209212        'invalid': _('Enter a whole number.'),
    210213        'max_value': _('Ensure this value is less than or equal to %(limit_value)s.'),
    class IntegerField(Field):  
    236239            raise ValidationError(self.error_messages['invalid'])
    237240        return value
    238241
     242    def widget_attrs(self, widget):
     243        attrs = super(IntegerField, self).widget_attrs(widget)
     244        if self.min_value:
     245            attrs['min'] = self.min_value
     246        if self.max_value:
     247            attrs['max'] = self.max_value
     248        return attrs
     249
     250
    239251class FloatField(IntegerField):
    240252    default_error_messages = {
    241253        'invalid': _('Enter a number.'),
    class FloatField(IntegerField):  
    257269            raise ValidationError(self.error_messages['invalid'])
    258270        return value
    259271
    260 class DecimalField(Field):
     272    def widget_attrs(self, widget):
     273        attrs = super(FloatField, self).widget_attrs(widget)
     274        attrs.setdefault('step', 'any')
     275        return attrs
     276
     277
     278class DecimalField(IntegerField):
    261279    default_error_messages = {
    262280        'invalid': _('Enter a number.'),
    263         'max_value': _('Ensure this value is less than or equal to %(limit_value)s.'),
    264         'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'),
    265281        'max_digits': _('Ensure that there are no more than %s digits in total.'),
    266282        'max_decimal_places': _('Ensure that there are no more than %s decimal places.'),
    267283        'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.')
    268284    }
    269285
    270286    def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
    271         self.max_value, self.min_value = max_value, min_value
    272287        self.max_digits, self.decimal_places = max_digits, decimal_places
    273         Field.__init__(self, *args, **kwargs)
    274 
    275         if max_value is not None:
    276             self.validators.append(validators.MaxValueValidator(max_value))
    277         if min_value is not None:
    278             self.validators.append(validators.MinValueValidator(min_value))
     288        super(DecimalField, self).__init__(max_value, min_value, *args, **kwargs)
    279289
    280290    def to_python(self, value):
    281291        """
    class DecimalField(Field):  
    324334            raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
    325335        return value
    326336
     337    def widget_attrs(self, widget):
     338        attrs = super(DecimalField, self).widget_attrs(widget)
     339        if self.max_digits:
     340            attrs['maxlength'] = self.max_digits + 1 # for the dot
     341        if self.decimal_places:
     342            attrs['step'] = '0.%s1' % ('0' * (self.decimal_places-1))
     343        return attrs
     344
     345
    327346class BaseTemporalField(Field):
    328347
    329348    def __init__(self, input_formats=None, *args, **kwargs):
    class RegexField(CharField):  
    460479    regex = property(_get_regex, _set_regex)
    461480
    462481class EmailField(CharField):
     482    widget = EmailInput
    463483    default_error_messages = {
    464484        'invalid': _('Enter a valid e-mail address.'),
    465485    }
    class ImageField(FileField):  
    576596        return f
    577597
    578598class URLField(CharField):
     599    widget = URLInput
    579600    default_error_messages = {
    580601        'invalid': _('Enter a valid URL.'),
    581602    }
  • django/forms/widgets.py

    diff --git a/django/forms/widgets.py b/django/forms/widgets.py
    index 763da0c..c6203f0 100644
    a b from django.utils.safestring import mark_safe  
    2222from django.utils import datetime_safe, formats, six
    2323
    2424__all__ = (
    25     'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput',
     25    'Media', 'MediaDefiningClass', 'Widget', 'TextInput',
     26    'EmailInput', 'URLInput', 'NumberInput', 'PasswordInput',
    2627    'HiddenInput', 'MultipleHiddenInput', 'ClearableFileInput',
    2728    'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput',
    2829    'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
    class TextInput(Input):  
    270271        super(TextInput, self).__init__(attrs)
    271272
    272273
     274class NumberInput(TextInput):
     275    input_type = 'number'
     276
     277
     278class EmailInput(TextInput):
     279    input_type = 'email'
     280
     281
     282class URLInput(TextInput):
     283    input_type = 'url'
     284
     285
    273286class PasswordInput(TextInput):
    274287    input_type = 'password'
    275288
  • docs/ref/forms/fields.txt

    diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
    index 2a8f449..5dcc98a 100644
    a b We've specified ``auto_id=False`` to simplify the output::  
    112112    >>> f = CommentForm(auto_id=False)
    113113    >>> print(f)
    114114    <tr><th>Your name:</th><td><input type="text" name="name" /></td></tr>
    115     <tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr>
     115    <tr><th>Your Web site:</th><td><input type="url" name="url" /></td></tr>
    116116    <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
    117117
    118118``initial``
    field is initialized to a particular value. For example::  
    135135    >>> f = CommentForm(auto_id=False)
    136136    >>> print(f)
    137137    <tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
    138     <tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr>
     138    <tr><th>Url:</th><td><input type="url" name="url" value="http://" /></td></tr>
    139139    <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
    140140
    141141You may be thinking, why not just pass a dictionary of the initial values as
    and the HTML output will include any validation errors::  
    150150    >>> f = CommentForm(default_data, auto_id=False)
    151151    >>> print(f)
    152152    <tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
    153     <tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr>
     153    <tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="url" name="url" value="http://" /></td></tr>
    154154    <tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr>
    155155
    156156This is why ``initial`` values are only displayed for unbound forms. For bound
    fields. We've specified ``auto_id=False`` to simplify the output::  
    217217    >>> print(f.as_ul()))
    218218    <li>Subject: <input type="text" name="subject" maxlength="100" /> <span class="helptext">100 characters max.</span></li>
    219219    <li>Message: <input type="text" name="message" /></li>
    220     <li>Sender: <input type="text" name="sender" /> A valid email address, please.</li>
     220    <li>Sender: <input type="email" name="sender" /> A valid email address, please.</li>
    221221    <li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
    222222    >>> print(f.as_p())
    223223    <p>Subject: <input type="text" name="subject" maxlength="100" /> <span class="helptext">100 characters max.</span></p>
    224224    <p>Message: <input type="text" name="message" /></p>
    225     <p>Sender: <input type="text" name="sender" /> A valid email address, please.</p>
     225    <p>Sender: <input type="email" name="sender" /> A valid email address, please.</p>
    226226    <p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
    227227
    228228``error_messages``
    For each field, we describe the default widget used if you don't specify  
    450450
    451451.. class:: DecimalField(**kwargs)
    452452
    453     * Default widget: ``TextInput``
     453    * Default widget: ``NumberInput``
    454454    * Empty value: ``None``
    455455    * Normalizes to: A Python ``decimal``.
    456456    * Validates that the given value is a decimal. Leading and trailing
    For each field, we describe the default widget used if you don't specify  
    485485
    486486.. class:: EmailField(**kwargs)
    487487
    488     * Default widget: ``TextInput``
     488    * Default widget: ``EmailInput``
    489489    * Empty value: ``''`` (an empty string)
    490490    * Normalizes to: A Unicode object.
    491491    * Validates that the given value is a valid email address, using a
    For each field, we describe the default widget used if you don't specify  
    576576
    577577.. class:: FloatField(**kwargs)
    578578
    579     * Default widget: ``TextInput``
     579    * Default widget: ``NumberInput``
    580580    * Empty value: ``None``
    581581    * Normalizes to: A Python float.
    582582    * Validates that the given value is an float. Leading and trailing
    For each field, we describe the default widget used if you don't specify  
    617617
    618618.. class:: IntegerField(**kwargs)
    619619
    620     * Default widget: ``TextInput``
     620    * Default widget: ``NumberInput``
    621621    * Empty value: ``None``
    622622    * Normalizes to: A Python integer or long integer.
    623623    * Validates that the given value is an integer. Leading and trailing
    For each field, we describe the default widget used if you don't specify  
    805805
    806806.. class:: URLField(**kwargs)
    807807
    808     * Default widget: ``TextInput``
     808    * Default widget: ``URLInput``
    809809    * Empty value: ``''`` (an empty string)
    810810    * Normalizes to: A Unicode object.
    811811    * Validates that the given value is a valid URL.
  • docs/ref/forms/widgets.txt

    diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
    index 1935cb2..b79c3d9 100644
    a b provided for each widget will be rendered exactly the same::  
    121121    >>> f = CommentForm(auto_id=False)
    122122    >>> f.as_table()
    123123    <tr><th>Name:</th><td><input type="text" name="name" /></td></tr>
    124     <tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
     124    <tr><th>Url:</th><td><input type="url" name="url"/></td></tr>
    125125    <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
    126126
    127127On a real Web page, you probably don't want every widget to look the same. You
    Django will then include the extra attributes in the rendered output:  
    144144    >>> f = CommentForm(auto_id=False)
    145145    >>> f.as_table()
    146146    <tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr>
    147     <tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
     147    <tr><th>Url:</th><td><input type="url" name="url"/></td></tr>
    148148    <tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
    149149
    150150.. _built-in widgets:
    commonly used groups of widgets:  
    179179
    180180    Text input: ``<input type='text' ...>``
    181181
     182``NumberInput``
     183~~~~~~~~~~~~~~~
     184
     185.. class:: NumberInput
     186
     187    .. versionadded:: 1.5
     188
     189    Text input: ``<input type='number' ...>``
     190
     191``EmailInput``
     192~~~~~~~~~~~~~~
     193
     194.. class:: EmailInput
     195
     196    .. versionadded:: 1.5
     197
     198    Text input: ``<input type='email' ...>``
     199
     200``URLInput``
     201~~~~~~~~~~~~
     202
     203.. class:: URLInput
     204
     205    .. versionadded:: 1.5
     206
     207    Text input: ``<input type='url' ...>``
     208
    182209``PasswordInput``
    183210~~~~~~~~~~~~~~~~~
    184211
  • docs/releases/1.5.txt

    diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
    index 6420239..ce0767b 100644
    a b Django 1.5 also includes several smaller improvements worth noting:  
    127127  configuration duplication. More information can be found in the
    128128  :func:`~django.contrib.auth.decorators.login_required` documentation.
    129129
     130* The default widgets for :class:`~django.forms.EmailField`,
     131  :class:`~django.forms.URLField`, :class:`~django.forms.IntegerField`,
     132  :class:`~django.forms.FloatField and :class:`~django.forms.DecimalField use
     133  the new type attributes available in HTML5 (type='email', type='url',
     134  type='number').
     135
    130136Backwards incompatible changes in 1.5
    131137=====================================
    132138
  • tests/modeltests/model_forms/tests.py

    diff --git a/tests/modeltests/model_forms/tests.py b/tests/modeltests/model_forms/tests.py
    index 038ce32..9898082 100644
    a b class OldFormForXTests(TestCase):  
    10881088<option value="%s">Joe Better</option>
    10891089<option value="%s">Mike Royko</option>
    10901090</select></p>
    1091 <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))
     1091<p><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" /></p>''' % (w_woodward.pk, w_bernstein.pk, bw.pk, w_royko.pk))
    10921092
    10931093        data = {
    10941094            'writer': six.text_type(w_woodward.pk),
    class OldFormForXTests(TestCase):  
    11061106<option value="%s">Joe Better</option>
    11071107<option value="%s">Mike Royko</option>
    11081108</select></p>
    1109 <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))
     1109<p><label for="id_age">Age:</label> <input type="number" name="age" value="65" id="id_age" /></p>''' % (w_woodward.pk, w_bernstein.pk, bw.pk, w_royko.pk))
    11101110
    11111111    def test_phone_number_field(self):
    11121112        f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
  • tests/modeltests/model_formsets/tests.py

    diff --git a/tests/modeltests/model_formsets/tests.py b/tests/modeltests/model_formsets/tests.py
    index e28560b..c4c60c0 100644
    a b class ModelFormsetTest(TestCase):  
    384384        self.assertEqual(len(formset.forms), 1)
    385385        self.assertHTMLEqual(formset.forms[0].as_p(),
    386386            '<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'
    387             '<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>')
     387            '<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>')
    388388
    389389        data = {
    390390            'form-TOTAL_FORMS': '1', # the number of forms rendered
    class ModelFormsetTest(TestCase):  
    407407        self.assertEqual(len(formset.forms), 2)
    408408        self.assertHTMLEqual(formset.forms[0].as_p(),
    409409            '<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'
    410             '<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)
     410            '<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)
    411411        self.assertHTMLEqual(formset.forms[1].as_p(),
    412412            '<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'
    413             '<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>')
     413            '<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>')
    414414
    415415        data = {
    416416            'form-TOTAL_FORMS': '2', # the number of forms rendered
    class ModelFormsetTest(TestCase):  
    550550        formset = AuthorBooksFormSet2(instance=author)
    551551        self.assertEqual(len(formset.forms), 1)
    552552        self.assertHTMLEqual(formset.forms[0].as_p(),
    553             '<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'
     553            '<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'
    554554            '<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>')
    555555
    556556        data = {
    class ModelFormsetTest(TestCase):  
    798798            '<option value="%d">Joe Perry at Giordanos</option>\n'
    799799            '<option value="%d">Jack Berry at Giordanos</option>\n'
    800800            '</select></p>\n'
    801             '<p><label for="id_form-0-age">Age:</label> <input type="text" name="form-0-age" id="id_form-0-age" /></p>'
     801            '<p><label for="id_form-0-age">Age:</label> <input type="number" name="form-0-age" id="id_form-0-age" /></p>'
    802802            % (owner1.auto_id, owner2.auto_id))
    803803
    804804        owner1 = Owner.objects.get(name='Joe Perry')
    class ModelFormsetTest(TestCase):  
    808808        formset = FormSet(instance=owner1)
    809809        self.assertEqual(len(formset.forms), 1)
    810810        self.assertHTMLEqual(formset.forms[0].as_p(),
    811             '<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>'
     811            '<p><label for="id_ownerprofile-0-age">Age:</label> <input type="number" name="ownerprofile-0-age" id="id_ownerprofile-0-age" /><input type="hidden" name="ownerprofile-0-owner" value="%d" id="id_ownerprofile-0-owner" /></p>'
    812812            % owner1.auto_id)
    813813
    814814        data = {
    class ModelFormsetTest(TestCase):  
    829829        formset = FormSet(instance=owner1)
    830830        self.assertEqual(len(formset.forms), 1)
    831831        self.assertHTMLEqual(formset.forms[0].as_p(),
    832             '<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>'
     832            '<p><label for="id_ownerprofile-0-age">Age:</label> <input type="number" 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>'
    833833            % owner1.auto_id)
    834834
    835835        data = {
    class ModelFormsetTest(TestCase):  
    985985        result = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?', '__DATETIME__', result)
    986986        self.assertHTMLEqual(result,
    987987            '<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'
    988             '<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>'
     988            '<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>'
    989989            % person.id)
    990990
    991991        # test for validation with callable defaults. Validations rely on hidden fields
  • tests/regressiontests/forms/tests/extra.py

    diff --git a/tests/regressiontests/forms/tests/extra.py b/tests/regressiontests/forms/tests/extra.py
    index 2ab5d40..d0350c0 100644
    a b class FormsExtraTestCase(TestCase, AssertFormErrorsMixin):  
    614614        f = CommentForm(data, auto_id=False, error_class=DivErrorList)
    615615        self.assertHTMLEqual(f.as_p(), """<p>Name: <input type="text" name="name" maxlength="50" /></p>
    616616<div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div>
    617 <p>Email: <input type="text" name="email" value="invalid" /></p>
     617<p>Email: <input type="email" name="email" value="invalid" /></p>
    618618<div class="errorlist"><div class="error">This field is required.</div></div>
    619619<p>Comment: <input type="text" name="comment" /></p>""")
    620620
  • tests/regressiontests/forms/tests/fields.py

    diff --git a/tests/regressiontests/forms/tests/fields.py b/tests/regressiontests/forms/tests/fields.py
    index 197ce1a..5774af2 100644
    a b def fix_os_paths(x):  
    5151
    5252class FieldsTests(SimpleTestCase):
    5353
     54    def assertWidgetRendersTo(self, field, to):
     55        class _Form(Form):
     56            f = field
     57        self.assertEqual(str(_Form()['f']), to)
     58
    5459    def test_field_sets_widget_is_required(self):
    5560        self.assertTrue(Field(required=True).widget.is_required)
    5661        self.assertFalse(Field(required=False).widget.is_required)
    class FieldsTests(SimpleTestCase):  
    124129
    125130    def test_integerfield_1(self):
    126131        f = IntegerField()
     132        self.assertWidgetRendersTo(f, '<input type="number" name="f" id="id_f" />')
    127133        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
    128134        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
    129135        self.assertEqual(1, f.clean('1'))
    class FieldsTests(SimpleTestCase):  
    158164
    159165    def test_integerfield_3(self):
    160166        f = IntegerField(max_value=10)
     167        self.assertWidgetRendersTo(f, '<input max="10" type="number" name="f" id="id_f" />')
    161168        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
    162169        self.assertEqual(1, f.clean(1))
    163170        self.assertEqual(10, f.clean(10))
    class FieldsTests(SimpleTestCase):  
    169176
    170177    def test_integerfield_4(self):
    171178        f = IntegerField(min_value=10)
     179        self.assertWidgetRendersTo(f, '<input id="id_f" type="number" name="f" min="10" />')
    172180        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
    173181        self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'", f.clean, 1)
    174182        self.assertEqual(10, f.clean(10))
    class FieldsTests(SimpleTestCase):  
    180188
    181189    def test_integerfield_5(self):
    182190        f = IntegerField(min_value=10, max_value=20)
     191        self.assertWidgetRendersTo(f, '<input id="id_f" max="20" type="number" name="f" min="10" />')
    183192        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
    184193        self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'", f.clean, 1)
    185194        self.assertEqual(10, f.clean(10))
    class FieldsTests(SimpleTestCase):  
    195204
    196205    def test_floatfield_1(self):
    197206        f = FloatField()
     207        self.assertWidgetRendersTo(f, '<input step="any" type="number" name="f" id="id_f" />')
    198208        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
    199209        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
    200210        self.assertEqual(1.0, f.clean('1'))
    class FieldsTests(SimpleTestCase):  
    221231
    222232    def test_floatfield_3(self):
    223233        f = FloatField(max_value=1.5, min_value=0.5)
     234        self.assertWidgetRendersTo(f, '<input step="any" name="f" min="0.5" max="1.5" type="number" id="id_f" />')
    224235        self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6')
    225236        self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4')
    226237        self.assertEqual(1.5, f.clean('1.5'))
    class FieldsTests(SimpleTestCase):  
    232243
    233244    def test_decimalfield_1(self):
    234245        f = DecimalField(max_digits=4, decimal_places=2)
     246        self.assertWidgetRendersTo(f, '<input id="id_f" step="0.01" type="number" name="f" maxlength="5" />')
    235247        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
    236248        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
    237249        self.assertEqual(f.clean('1'), Decimal("1"))
    class FieldsTests(SimpleTestCase):  
    277289
    278290    def test_decimalfield_3(self):
    279291        f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5'))
     292        self.assertWidgetRendersTo(f, '<input step="0.01" name="f" min="0.5" max="1.5" maxlength="5" type="number" id="id_f" />')
    280293        self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6')
    281294        self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4')
    282295        self.assertEqual(f.clean('1.5'), Decimal("1.5"))
    class FieldsTests(SimpleTestCase):  
    499512
    500513    def test_emailfield_1(self):
    501514        f = EmailField()
     515        self.assertWidgetRendersTo(f, '<input type="email" name="f" id="id_f" />')
    502516        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
    503517        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
    504518        self.assertEqual('person@example.com', f.clean('person@example.com'))
    class FieldsTests(SimpleTestCase):  
    537551
    538552    def test_emailfield_3(self):
    539553        f = EmailField(min_length=10, max_length=15)
     554        self.assertWidgetRendersTo(f, '<input id="id_f" type="email" name="f" maxlength="15" />')
    540555        self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 10 characters (it has 9).'", f.clean, 'a@foo.com')
    541556        self.assertEqual('alf@foo.com', f.clean('alf@foo.com'))
    542557        self.assertRaisesMessage(ValidationError, "'Ensure this value has at most 15 characters (it has 20).'", f.clean, 'alf123456788@foo.com')
    class FieldsTests(SimpleTestCase):  
    577592
    578593    def test_urlfield_1(self):
    579594        f = URLField()
     595        self.assertWidgetRendersTo(f, '<input type="url" name="f" id="id_f" />')
    580596        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
    581597        self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
    582598        self.assertEqual('http://localhost/', f.clean('http://localhost'))
    class FieldsTests(SimpleTestCase):  
    628644
    629645    def test_urlfield_5(self):
    630646        f = URLField(min_length=15, max_length=20)
     647        self.assertWidgetRendersTo(f, '<input id="id_f" type="url" name="f" maxlength="20" />')
    631648        self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 15 characters (it has 13).'", f.clean, 'http://f.com')
    632649        self.assertEqual('http://example.com/', f.clean('http://example.com'))
    633650        self.assertRaisesMessage(ValidationError, "'Ensure this value has at most 20 characters (it has 38).'", f.clean, 'http://abcdefghijklmnopqrstuvwxyz.com')
  • tests/regressiontests/forms/tests/forms.py

    diff --git a/tests/regressiontests/forms/tests/forms.py b/tests/regressiontests/forms/tests/forms.py
    index a8a28ba..a34fbcd 100644
    a b class FormsTestCase(TestCase):  
    248248            get_spam = BooleanField()
    249249
    250250        f = SignupForm(auto_id=False)
    251         self.assertHTMLEqual(str(f['email']), '<input type="text" name="email" />')
     251        self.assertHTMLEqual(str(f['email']), '<input type="email" name="email" />')
    252252        self.assertHTMLEqual(str(f['get_spam']), '<input type="checkbox" name="get_spam" />')
    253253
    254254        f = SignupForm({'email': 'test@example.com', 'get_spam': True}, auto_id=False)
    255         self.assertHTMLEqual(str(f['email']), '<input type="text" name="email" value="test@example.com" />')
     255        self.assertHTMLEqual(str(f['email']), '<input type="email" name="email" value="test@example.com" />')
    256256        self.assertHTMLEqual(str(f['get_spam']), '<input checked="checked" type="checkbox" name="get_spam" />')
    257257
    258258        # 'True' or 'true' should be rendered without a value attribute
    class FormsTestCase(TestCase):  
    17361736<option value="2">Yes</option>
    17371737<option value="3">No</option>
    17381738</select></li>
    1739 <li><label for="id_email">Email:</label> <input type="text" name="email" id="id_email" /></li>
    1740 <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>""")
     1739<li><label for="id_email">Email:</label> <input type="email" name="email" id="id_email" /></li>
     1740<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>""")
    17411741
    17421742        self.assertHTMLEqual(p.as_p(), """<ul class="errorlist"><li>This field is required.</li></ul>
    17431743<p class="required error"><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p>
    class FormsTestCase(TestCase):  
    17461746<option value="2">Yes</option>
    17471747<option value="3">No</option>
    17481748</select></p>
    1749 <p><label for="id_email">Email:</label> <input type="text" name="email" id="id_email" /></p>
     1749<p><label for="id_email">Email:</label> <input type="email" name="email" id="id_email" /></p>
    17501750<ul class="errorlist"><li>This field is required.</li></ul>
    1751 <p class="required error"><label for="id_age">Age:</label> <input type="text" name="age" id="id_age" /></p>""")
     1751<p class="required error"><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" /></p>""")
    17521752
    17531753        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>
    17541754<tr class="required"><th><label for="id_is_cool">Is cool:</label></th><td><select name="is_cool" id="id_is_cool">
    class FormsTestCase(TestCase):  
    17561756<option value="2">Yes</option>
    17571757<option value="3">No</option>
    17581758</select></td></tr>
    1759 <tr><th><label for="id_email">Email:</label></th><td><input type="text" name="email" id="id_email" /></td></tr>
    1760 <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>""")
     1759<tr><th><label for="id_email">Email:</label></th><td><input type="email" name="email" id="id_email" /></td></tr>
     1760<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>""")
    17611761
    17621762    def test_label_split_datetime_not_displayed(self):
    17631763        class EventForm(Form):
  • tests/regressiontests/forms/tests/formsets.py

    diff --git a/tests/regressiontests/forms/tests/formsets.py b/tests/regressiontests/forms/tests/formsets.py
    index 3decd1f..79f3f2f 100644
    a b class FormsFormsetTestCase(TestCase):  
    5151        formset = ChoiceFormSet(auto_id=False, prefix='choices')
    5252        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" />
    5353<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr>
    54 <tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr>""")
     54<tr><th>Votes:</th><td><input type="number" name="choices-0-votes" /></td></tr>""")
    5555
    5656        # On thing to note is that there needs to be a special value in the data. This
    5757        # value tells the FormSet how many forms were displayed so it can tell how
    class FormsFormsetTestCase(TestCase):  
    135135            form_output.append(form.as_ul())
    136136
    137137        self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
    138 <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
     138<li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
    139139<li>Choice: <input type="text" name="choices-1-choice" /></li>
    140 <li>Votes: <input type="text" name="choices-1-votes" /></li>""")
     140<li>Votes: <input type="number" name="choices-1-votes" /></li>""")
    141141
    142142        # Let's simulate what would happen if we submitted this form.
    143143
    class FormsFormsetTestCase(TestCase):  
    208208            form_output.append(form.as_ul())
    209209
    210210        self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" /></li>
    211 <li>Votes: <input type="text" name="choices-0-votes" /></li>
     211<li>Votes: <input type="number" name="choices-0-votes" /></li>
    212212<li>Choice: <input type="text" name="choices-1-choice" /></li>
    213 <li>Votes: <input type="text" name="choices-1-votes" /></li>
     213<li>Votes: <input type="number" name="choices-1-votes" /></li>
    214214<li>Choice: <input type="text" name="choices-2-choice" /></li>
    215 <li>Votes: <input type="text" name="choices-2-votes" /></li>""")
     215<li>Votes: <input type="number" name="choices-2-votes" /></li>""")
    216216
    217217        # Since we displayed every form as blank, we will also accept them back as blank.
    218218        # This may seem a little strange, but later we will show how to require a minimum
    class FormsFormsetTestCase(TestCase):  
    299299            form_output.append(form.as_ul())
    300300
    301301        self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
    302 <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
     302<li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
    303303<li>Choice: <input type="text" name="choices-1-choice" /></li>
    304 <li>Votes: <input type="text" name="choices-1-votes" /></li>
     304<li>Votes: <input type="number" name="choices-1-votes" /></li>
    305305<li>Choice: <input type="text" name="choices-2-choice" /></li>
    306 <li>Votes: <input type="text" name="choices-2-votes" /></li>
     306<li>Votes: <input type="number" name="choices-2-votes" /></li>
    307307<li>Choice: <input type="text" name="choices-3-choice" /></li>
    308 <li>Votes: <input type="text" name="choices-3-votes" /></li>""")
     308<li>Votes: <input type="number" name="choices-3-votes" /></li>""")
    309309
    310310        # Make sure retrieving an empty form works, and it shows up in the form list
    311311
    312312        self.assertTrue(formset.empty_form.empty_permitted)
    313313        self.assertHTMLEqual(formset.empty_form.as_ul(), """<li>Choice: <input type="text" name="choices-__prefix__-choice" /></li>
    314 <li>Votes: <input type="text" name="choices-__prefix__-votes" /></li>""")
     314<li>Votes: <input type="number" name="choices-__prefix__-votes" /></li>""")
    315315
    316316    def test_formset_with_deletion(self):
    317317        # FormSets with deletion ######################################################
    class FormsFormsetTestCase(TestCase):  
    329329            form_output.append(form.as_ul())
    330330
    331331        self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
    332 <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
     332<li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
    333333<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
    334334<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
    335 <li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
     335<li>Votes: <input type="number" name="choices-1-votes" value="900" /></li>
    336336<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
    337337<li>Choice: <input type="text" name="choices-2-choice" /></li>
    338 <li>Votes: <input type="text" name="choices-2-votes" /></li>
     338<li>Votes: <input type="number" name="choices-2-votes" /></li>
    339339<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>""")
    340340
    341341        # To delete something, we just need to set that form's special delete field to
    class FormsFormsetTestCase(TestCase):  
    426426            form_output.append(form.as_ul())
    427427
    428428        self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
    429 <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
    430 <li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li>
     429<li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
     430<li>Order: <input type="number" name="choices-0-ORDER" value="1" /></li>
    431431<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
    432 <li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
    433 <li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li>
     432<li>Votes: <input type="number" name="choices-1-votes" value="900" /></li>
     433<li>Order: <input type="number" name="choices-1-ORDER" value="2" /></li>
    434434<li>Choice: <input type="text" name="choices-2-choice" /></li>
    435 <li>Votes: <input type="text" name="choices-2-votes" /></li>
    436 <li>Order: <input type="text" name="choices-2-ORDER" /></li>""")
     435<li>Votes: <input type="number" name="choices-2-votes" /></li>
     436<li>Order: <input type="number" name="choices-2-ORDER" /></li>""")
    437437
    438438        data = {
    439439            'choices-TOTAL_FORMS': '3', # the number of forms rendered
    class FormsFormsetTestCase(TestCase):  
    537537            form_output.append(form.as_ul())
    538538
    539539        self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
    540 <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
    541 <li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li>
     540<li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>
     541<li>Order: <input type="number" name="choices-0-ORDER" value="1" /></li>
    542542<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
    543543<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
    544 <li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
    545 <li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li>
     544<li>Votes: <input type="number" name="choices-1-votes" value="900" /></li>
     545<li>Order: <input type="number" name="choices-1-ORDER" value="2" /></li>
    546546<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
    547547<li>Choice: <input type="text" name="choices-2-choice" value="The Decemberists" /></li>
    548 <li>Votes: <input type="text" name="choices-2-votes" value="500" /></li>
    549 <li>Order: <input type="text" name="choices-2-ORDER" value="3" /></li>
     548<li>Votes: <input type="number" name="choices-2-votes" value="500" /></li>
     549<li>Order: <input type="number" name="choices-2-ORDER" value="3" /></li>
    550550<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>
    551551<li>Choice: <input type="text" name="choices-3-choice" /></li>
    552 <li>Votes: <input type="text" name="choices-3-votes" /></li>
    553 <li>Order: <input type="text" name="choices-3-ORDER" /></li>
     552<li>Votes: <input type="number" name="choices-3-votes" /></li>
     553<li>Order: <input type="number" name="choices-3-ORDER" /></li>
    554554<li>Delete: <input type="checkbox" name="choices-3-DELETE" /></li>""")
    555555
    556556        # Let's delete Fergie, and put The Decemberists ahead of Calexico.
    class FormsetAsFooTests(TestCase):  
    866866        formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
    867867        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" />
    868868<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" value="Calexico" /></td></tr>
    869 <tr><th>Votes:</th><td><input type="text" name="choices-0-votes" value="100" /></td></tr>""")
     869<tr><th>Votes:</th><td><input type="number" name="choices-0-votes" value="100" /></td></tr>""")
    870870
    871871    def test_as_p(self):
    872872        formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
    873873        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" />
    874874<p>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></p>
    875 <p>Votes: <input type="text" name="choices-0-votes" value="100" /></p>""")
     875<p>Votes: <input type="number" name="choices-0-votes" value="100" /></p>""")
    876876
    877877    def test_as_ul(self):
    878878        formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
    879879        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" />
    880880<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
    881 <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>""")
     881<li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>""")
    882882
    883883
    884884# Regression test for #11418 #################################################
  • tests/regressiontests/generic_inline_admin/tests.py

    diff --git a/tests/regressiontests/generic_inline_admin/tests.py b/tests/regressiontests/generic_inline_admin/tests.py
    index fea30b4..f03641d 100644
    a b class GenericAdminViewTest(TestCase):  
    102102        # Works with no queryset
    103103        formset = EpisodeMediaFormSet(instance=e)
    104104        self.assertEqual(len(formset.forms), 5)
    105         self.assertHTMLEqual(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="text" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/podcast.mp3" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>' % self.mp3_media_pk)
    106         self.assertHTMLEqual(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>' % self.png_media_pk)
    107         self.assertHTMLEqual(formset.forms[2].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-2-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-2-url" type="text" name="generic_inline_admin-media-content_type-object_id-2-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-2-id" id="id_generic_inline_admin-media-content_type-object_id-2-id" /></p>')
     105        self.assertHTMLEqual(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="url" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/podcast.mp3" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>' % self.mp3_media_pk)
     106        self.assertHTMLEqual(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="url" name="generic_inline_admin-media-content_type-object_id-1-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>' % self.png_media_pk)
     107        self.assertHTMLEqual(formset.forms[2].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-2-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-2-url" type="url" name="generic_inline_admin-media-content_type-object_id-2-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-2-id" id="id_generic_inline_admin-media-content_type-object_id-2-id" /></p>')
    108108
    109109        # A queryset can be used to alter display ordering
    110110        formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.order_by('url'))
    111111        self.assertEqual(len(formset.forms), 5)
    112         self.assertHTMLEqual(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="text" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>' % self.png_media_pk)
    113         self.assertHTMLEqual(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" value="http://example.com/podcast.mp3" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>' % self.mp3_media_pk)
    114         self.assertHTMLEqual(formset.forms[2].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-2-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-2-url" type="text" name="generic_inline_admin-media-content_type-object_id-2-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-2-id" id="id_generic_inline_admin-media-content_type-object_id-2-id" /></p>')
     112        self.assertHTMLEqual(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="url" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>' % self.png_media_pk)
     113        self.assertHTMLEqual(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="url" name="generic_inline_admin-media-content_type-object_id-1-url" value="http://example.com/podcast.mp3" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>' % self.mp3_media_pk)
     114        self.assertHTMLEqual(formset.forms[2].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-2-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-2-url" type="url" name="generic_inline_admin-media-content_type-object_id-2-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-2-id" id="id_generic_inline_admin-media-content_type-object_id-2-id" /></p>')
    115115
    116116        # Works with a queryset that omits items
    117117        formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.filter(url__endswith=".png"))
    118118        self.assertEqual(len(formset.forms), 4)
    119         self.assertHTMLEqual(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="text" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>' % self.png_media_pk)
    120         self.assertHTMLEqual(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>')
     119        self.assertHTMLEqual(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="url" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>' % self.png_media_pk)
     120        self.assertHTMLEqual(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="url" name="generic_inline_admin-media-content_type-object_id-1-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>')
    121121
    122122    def testGenericInlineFormsetFactory(self):
    123123        # Regression test for #10522.
  • tests/regressiontests/i18n/tests.py

    diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
    index bce3d61..60e234e 100644
    a b class FormattingTests(TestCase):  
    634634            self.assertEqual(True, form6.is_valid())
    635635            self.assertHTMLEqual(
    636636                form6.as_ul(),
    637                 '<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" value="acme" maxlength="50" /></li>\n<li><label for="id_date_added">Date added:</label> <input type="text" name="date_added" value="31.12.2009 06:00:00" id="id_date_added" /></li>\n<li><label for="id_cents_paid">Cents paid:</label> <input type="text" name="cents_paid" value="59,47" id="id_cents_paid" /></li>\n<li><label for="id_products_delivered">Products delivered:</label> <input type="text" name="products_delivered" value="12000" id="id_products_delivered" /></li>'
     637                '<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" value="acme" maxlength="50" /></li>\n<li><label for="id_date_added">Date added:</label> <input type="text" name="date_added" value="31.12.2009 06:00:00" id="id_date_added" /></li>\n<li><label for="id_cents_paid">Cents paid:</label> <input type="number" name="cents_paid" value="59,47" maxlength="5" step="0.01" id="id_cents_paid" /></li>\n<li><label for="id_products_delivered">Products delivered:</label> <input type="number" name="products_delivered" value="12000" id="id_products_delivered" /></li>'
    638638            )
    639639            self.assertEqual(localize_input(datetime.datetime(2009, 12, 31, 6, 0, 0)), '31.12.2009 06:00:00')
    640640            self.assertEqual(datetime.datetime(2009, 12, 31, 6, 0, 0), form6.cleaned_data['date_added'])
    641641            with self.settings(USE_THOUSAND_SEPARATOR=True):
    642642                # Checking for the localized "products_delivered" field
    643                 self.assertTrue('<input type="text" name="products_delivered" value="12.000" id="id_products_delivered" />' in form6.as_ul())
     643                self.assertTrue('<input type="number" name="products_delivered" value="12.000" id="id_products_delivered" />' in form6.as_ul())
    644644
    645645    def test_iter_format_modules(self):
    646646        """
Back to Top