mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Refs #32819 -- Added id to ErrorList class and template.
This commit is contained in:
		| @@ -298,7 +298,10 @@ class BaseForm(RenderableFormMixin): | ||||
|                         error_class="nonfield", renderer=self.renderer | ||||
|                     ) | ||||
|                 else: | ||||
|                     self._errors[field] = self.error_class(renderer=self.renderer) | ||||
|                     self._errors[field] = self.error_class( | ||||
|                         renderer=self.renderer, | ||||
|                         field_id=self[field].auto_id, | ||||
|                     ) | ||||
|             self._errors[field].extend(error_list) | ||||
|             if field in self.cleaned_data: | ||||
|                 del self.cleaned_data[field] | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| {% if errors %}<ul class="{{ error_class }}">{% for error in errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %} | ||||
| {% if errors %}<ul class="{{ error_class }}"{% if errors.field_id %} id="{{ errors.field_id }}_error"{% endif %}>{% for error in errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %} | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| {% if errors %}<ul class="{{ error_class }}">{% for error in errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %} | ||||
| {% if errors %}<ul class="{{ error_class }}"{% if errors.field_id %} id="{{ errors.field_id }}_error"{% endif %}>{% for error in errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %} | ||||
| @@ -147,7 +147,7 @@ class ErrorList(UserList, list, RenderableErrorMixin): | ||||
|     template_name_text = "django/forms/errors/list/text.txt" | ||||
|     template_name_ul = "django/forms/errors/list/ul.html" | ||||
|  | ||||
|     def __init__(self, initlist=None, error_class=None, renderer=None): | ||||
|     def __init__(self, initlist=None, error_class=None, renderer=None, field_id=None): | ||||
|         super().__init__(initlist) | ||||
|  | ||||
|         if error_class is None: | ||||
| @@ -155,6 +155,7 @@ class ErrorList(UserList, list, RenderableErrorMixin): | ||||
|         else: | ||||
|             self.error_class = "errorlist {}".format(error_class) | ||||
|         self.renderer = renderer or get_default_renderer() | ||||
|         self.field_id = field_id | ||||
|  | ||||
|     def as_data(self): | ||||
|         return ValidationError(self.data).error_list | ||||
|   | ||||
| @@ -1025,13 +1025,17 @@ method you're using: | ||||
| Customizing the error list format | ||||
| --------------------------------- | ||||
|  | ||||
| .. class:: ErrorList(initlist=None, error_class=None, renderer=None) | ||||
| .. class:: ErrorList(initlist=None, error_class=None, renderer=None, field_id=None) | ||||
|  | ||||
|     By default, forms use ``django.forms.utils.ErrorList`` to format validation | ||||
|     errors. ``ErrorList`` is a list like object where ``initlist`` is the | ||||
|     list of errors. In addition this class has the following attributes and | ||||
|     methods. | ||||
|  | ||||
|     .. versionchanged:: 5.2 | ||||
|  | ||||
|         The ``field_id`` argument was added. | ||||
|  | ||||
|     .. attribute:: error_class | ||||
|  | ||||
|         The CSS classes to be used when rendering the error list. Any provided | ||||
| @@ -1043,6 +1047,16 @@ Customizing the error list format | ||||
|         Defaults to ``None`` which means to use the default renderer | ||||
|         specified by the :setting:`FORM_RENDERER` setting. | ||||
|  | ||||
|     .. attribute:: field_id | ||||
|  | ||||
|         .. versionadded:: 5.2 | ||||
|  | ||||
|         An ``id`` for the field for which the errors relate. This allows an | ||||
|         HTML ``id`` attribute to be added in the error template and is useful | ||||
|         to associate the errors with the field. The default template uses the | ||||
|         format ``id="{{ field_id }}_error"`` and a value is provided by | ||||
|         :meth:`.Form.add_error` using the field's :attr:`~.BoundField.auto_id`. | ||||
|  | ||||
|     .. attribute:: template_name | ||||
|  | ||||
|         The name of the template used when calling ``__str__`` or | ||||
|   | ||||
| @@ -249,6 +249,10 @@ Forms | ||||
| * The new :class:`~django.forms.TelInput` form widget is for entering telephone | ||||
|   numbers and renders as ``<input type="tel" ...>``. | ||||
|  | ||||
| * The new ``field_id`` argument for :class:`~django.forms.ErrorList` allows an | ||||
|   HTML ``id`` attribute to be added in the error template. See | ||||
|   :attr:`.ErrorList.field_id` for details. | ||||
|  | ||||
| Generic Views | ||||
| ~~~~~~~~~~~~~ | ||||
|  | ||||
|   | ||||
| @@ -249,7 +249,8 @@ class FormsErrorMessagesTestCase(SimpleTestCase, AssertFormErrorsMixin): | ||||
|         form1 = TestForm({"first_name": "John"}) | ||||
|         self.assertHTMLEqual( | ||||
|             str(form1["last_name"].errors), | ||||
|             '<ul class="errorlist"><li>This field is required.</li></ul>', | ||||
|             '<ul class="errorlist" id="id_last_name_error"><li>' | ||||
|             "This field is required.</li></ul>", | ||||
|         ) | ||||
|         self.assertHTMLEqual( | ||||
|             str(form1.errors["__all__"]), | ||||
| @@ -280,7 +281,7 @@ class FormsErrorMessagesTestCase(SimpleTestCase, AssertFormErrorsMixin): | ||||
|         f = SomeForm({"field": "<script>"}) | ||||
|         self.assertHTMLEqual( | ||||
|             t.render(Context({"form": f})), | ||||
|             '<ul class="errorlist"><li>field<ul class="errorlist">' | ||||
|             '<ul class="errorlist"><li>field<ul class="errorlist" id="id_field_error">' | ||||
|             "<li>Select a valid choice. <script> is not one of the " | ||||
|             "available choices.</li></ul></li></ul>", | ||||
|         ) | ||||
| @@ -291,7 +292,7 @@ class FormsErrorMessagesTestCase(SimpleTestCase, AssertFormErrorsMixin): | ||||
|         f = SomeForm({"field": ["<script>"]}) | ||||
|         self.assertHTMLEqual( | ||||
|             t.render(Context({"form": f})), | ||||
|             '<ul class="errorlist"><li>field<ul class="errorlist">' | ||||
|             '<ul class="errorlist"><li>field<ul class="errorlist" id="id_field_error">' | ||||
|             "<li>Select a valid choice. <script> is not one of the " | ||||
|             "available choices.</li></ul></li></ul>", | ||||
|         ) | ||||
| @@ -302,7 +303,7 @@ class FormsErrorMessagesTestCase(SimpleTestCase, AssertFormErrorsMixin): | ||||
|         f = SomeForm({"field": ["<script>"]}) | ||||
|         self.assertHTMLEqual( | ||||
|             t.render(Context({"form": f})), | ||||
|             '<ul class="errorlist"><li>field<ul class="errorlist">' | ||||
|             '<ul class="errorlist"><li>field<ul class="errorlist" id="id_field_error">' | ||||
|             "<li>“<script>” is not a valid value.</li>" | ||||
|             "</ul></li></ul>", | ||||
|         ) | ||||
|   | ||||
| @@ -181,53 +181,55 @@ class FormsTestCase(SimpleTestCase): | ||||
|         self.assertHTMLEqual( | ||||
|             str(p), | ||||
|             '<div><label for="id_first_name">First name:</label>' | ||||
|             '<ul class="errorlist"><li>This field is required.</li></ul>' | ||||
|             '<input type="text" name="first_name" aria-invalid="true" required ' | ||||
|             'id="id_first_name"></div>' | ||||
|             '<ul class="errorlist" id="id_first_name_error"><li>This field is required.' | ||||
|             '</li></ul><input type="text" name="first_name" aria-invalid="true" ' | ||||
|             'required id="id_first_name"></div>' | ||||
|             '<div><label for="id_last_name">Last name:</label>' | ||||
|             '<ul class="errorlist"><li>This field is required.</li></ul>' | ||||
|             '<input type="text" name="last_name" aria-invalid="true" required ' | ||||
|             'id="id_last_name"></div><div>' | ||||
|             '<ul class="errorlist" id="id_last_name_error"><li>This field is required.' | ||||
|             '</li></ul><input type="text" name="last_name" aria-invalid="true" ' | ||||
|             'required id="id_last_name"></div><div>' | ||||
|             '<label for="id_birthday">Birthday:</label>' | ||||
|             '<ul class="errorlist"><li>This field is required.</li></ul>' | ||||
|             '<input type="text" name="birthday" aria-invalid="true" required ' | ||||
|             '<ul class="errorlist" id="id_birthday_error"><li>This field is required.' | ||||
|             '</li></ul><input type="text" name="birthday" aria-invalid="true" required ' | ||||
|             'id="id_birthday"></div>', | ||||
|         ) | ||||
|         self.assertHTMLEqual( | ||||
|             p.as_table(), | ||||
|             """<tr><th><label for="id_first_name">First name:</label></th><td> | ||||
| <ul class="errorlist"><li>This field is required.</li></ul> | ||||
| <ul class="errorlist" id="id_first_name_error"><li>This field is required.</li></ul> | ||||
| <input type="text" name="first_name" id="id_first_name" aria-invalid="true" required> | ||||
| </td></tr><tr><th><label for="id_last_name">Last name:</label></th> | ||||
| <td><ul class="errorlist"><li>This field is required.</li></ul> | ||||
| <td><ul class="errorlist" id="id_last_name_error"><li>This field is required.</li></ul> | ||||
| <input type="text" name="last_name" id="id_last_name" aria-invalid="true" required> | ||||
| </td></tr><tr><th><label for="id_birthday">Birthday:</label></th> | ||||
| <td><ul class="errorlist"><li>This field is required.</li></ul> | ||||
| <td><ul class="errorlist" id="id_birthday_error"><li>This field is required.</li></ul> | ||||
| <input type="text" name="birthday" id="id_birthday" aria-invalid="true" required> | ||||
| </td></tr>""", | ||||
|         ) | ||||
|         self.assertHTMLEqual( | ||||
|             p.as_ul(), | ||||
|             """<li><ul class="errorlist"><li>This field is required.</li></ul> | ||||
|             """<li><ul class="errorlist" id="id_first_name_error"> | ||||
| <li>This field is required.</li></ul> | ||||
| <label for="id_first_name">First name:</label> | ||||
| <input type="text" name="first_name" id="id_first_name" aria-invalid="true" required> | ||||
| </li><li><ul class="errorlist"><li>This field is required.</li></ul> | ||||
| <label for="id_last_name">Last name:</label> | ||||
| </li><li><ul class="errorlist" id="id_last_name_error"><li>This field is required.</li> | ||||
| </ul><label for="id_last_name">Last name:</label> | ||||
| <input type="text" name="last_name" id="id_last_name" aria-invalid="true" required> | ||||
| </li><li><ul class="errorlist"><li>This field is required.</li></ul> | ||||
| <label for="id_birthday">Birthday:</label> | ||||
| </li><li><ul class="errorlist" id="id_birthday_error"><li>This field is required.</li> | ||||
| </ul><label for="id_birthday">Birthday:</label> | ||||
| <input type="text" name="birthday" id="id_birthday" aria-invalid="true" required> | ||||
| </li>""", | ||||
|         ) | ||||
|         self.assertHTMLEqual( | ||||
|             p.as_p(), | ||||
|             """<ul class="errorlist"><li>This field is required.</li></ul> | ||||
|             """<ul class="errorlist" id="id_first_name_error"><li> | ||||
| This field is required.</li></ul> | ||||
| <p><label for="id_first_name">First name:</label> | ||||
| <input type="text" name="first_name" id="id_first_name" aria-invalid="true" required> | ||||
| </p><ul class="errorlist"><li>This field is required.</li></ul> | ||||
| </p><ul class="errorlist" id="id_last_name_error"><li>This field is required.</li></ul> | ||||
| <p><label for="id_last_name">Last name:</label> | ||||
| <input type="text" name="last_name" id="id_last_name" aria-invalid="true" required> | ||||
| </p><ul class="errorlist"><li>This field is required.</li></ul> | ||||
| </p><ul class="errorlist" id="id_birthday_error"><li>This field is required.</li></ul> | ||||
| <p><label for="id_birthday">Birthday:</label> | ||||
| <input type="text" name="birthday" id="id_birthday" aria-invalid="true" required> | ||||
| </p>""", | ||||
| @@ -235,16 +237,16 @@ class FormsTestCase(SimpleTestCase): | ||||
|         self.assertHTMLEqual( | ||||
|             p.as_div(), | ||||
|             '<div><label for="id_first_name">First name:</label>' | ||||
|             '<ul class="errorlist"><li>This field is required.</li></ul>' | ||||
|             '<input type="text" name="first_name" aria-invalid="true" required ' | ||||
|             'id="id_first_name"></div>' | ||||
|             '<ul class="errorlist" id="id_first_name_error"><li>This field is required.' | ||||
|             '</li></ul><input type="text" name="first_name" aria-invalid="true" ' | ||||
|             'required id="id_first_name"></div>' | ||||
|             '<div><label for="id_last_name">Last name:</label>' | ||||
|             '<ul class="errorlist"><li>This field is required.</li></ul>' | ||||
|             '<input type="text" name="last_name" aria-invalid="true" required ' | ||||
|             'id="id_last_name"></div><div>' | ||||
|             '<ul class="errorlist" id="id_last_name_error"><li>This field is required.' | ||||
|             '</li></ul><input type="text" name="last_name" aria-invalid="true" ' | ||||
|             'required id="id_last_name"></div><div>' | ||||
|             '<label for="id_birthday">Birthday:</label>' | ||||
|             '<ul class="errorlist"><li>This field is required.</li></ul>' | ||||
|             '<input type="text" name="birthday" aria-invalid="true" required ' | ||||
|             '<ul class="errorlist" id="id_birthday_error"><li>This field is required.' | ||||
|             '</li></ul><input type="text" name="birthday" aria-invalid="true" required ' | ||||
|             'id="id_birthday"></div>', | ||||
|         ) | ||||
|  | ||||
| @@ -387,7 +389,8 @@ class FormsTestCase(SimpleTestCase): | ||||
|         self.assertEqual(p["first_name"].errors, ["This field is required."]) | ||||
|         self.assertHTMLEqual( | ||||
|             p["first_name"].errors.as_ul(), | ||||
|             '<ul class="errorlist"><li>This field is required.</li></ul>', | ||||
|             '<ul class="errorlist" id="id_first_name_error">' | ||||
|             "<li>This field is required.</li></ul>", | ||||
|         ) | ||||
|         self.assertEqual(p["first_name"].errors.as_text(), "* This field is required.") | ||||
|  | ||||
| @@ -3706,7 +3709,7 @@ Options: <select multiple name="options" aria-invalid="true" required> | ||||
|         self.assertHTMLEqual( | ||||
|             p.as_ul(), | ||||
|             """ | ||||
|             <li class="required error"><ul class="errorlist"> | ||||
|             <li class="required error"><ul class="errorlist" id="id_name_error"> | ||||
|             <li>This field is required.</li></ul> | ||||
|             <label class="required" for="id_name">Name:</label> | ||||
|             <input type="text" name="name" id="id_name" aria-invalid="true" required> | ||||
| @@ -3719,7 +3722,7 @@ Options: <select multiple name="options" aria-invalid="true" required> | ||||
|             </select></li> | ||||
|             <li><label for="id_email">Email:</label> | ||||
|             <input type="email" name="email" id="id_email" maxlength="320"></li> | ||||
|             <li class="required error"><ul class="errorlist"> | ||||
|             <li class="required error"><ul class="errorlist" id="id_age_error"> | ||||
|             <li>This field is required.</li></ul> | ||||
|             <label class="required" for="id_age">Age:</label> | ||||
|             <input type="number" name="age" id="id_age" aria-invalid="true" required> | ||||
| @@ -3729,8 +3732,8 @@ Options: <select multiple name="options" aria-invalid="true" required> | ||||
|         self.assertHTMLEqual( | ||||
|             p.as_p(), | ||||
|             """ | ||||
|             <ul class="errorlist"><li>This field is required.</li></ul> | ||||
|             <p class="required error"> | ||||
|             <ul class="errorlist" id="id_name_error"><li>This field is required.</li> | ||||
|             </ul><p class="required error"> | ||||
|             <label class="required" for="id_name">Name:</label> | ||||
|             <input type="text" name="name" id="id_name" aria-invalid="true" required> | ||||
|             </p><p class="required"> | ||||
| @@ -3742,17 +3745,17 @@ Options: <select multiple name="options" aria-invalid="true" required> | ||||
|             </select></p> | ||||
|             <p><label for="id_email">Email:</label> | ||||
|             <input type="email" name="email" id="id_email" maxlength="320"></p> | ||||
|             <ul class="errorlist"><li>This field is required.</li></ul> | ||||
|             <p class="required error"><label class="required" for="id_age">Age:</label> | ||||
|             <input type="number" name="age" id="id_age" aria-invalid="true" required> | ||||
|             </p>""", | ||||
|             <ul class="errorlist" id="id_age_error"><li>This field is required.</li> | ||||
|             </ul><p class="required error"><label class="required" for="id_age"> | ||||
|             Age:</label><input type="number" name="age" id="id_age" aria-invalid="true" | ||||
|             required></p>""", | ||||
|         ) | ||||
|  | ||||
|         self.assertHTMLEqual( | ||||
|             p.as_table(), | ||||
|             """<tr class="required error"> | ||||
| <th><label class="required" for="id_name">Name:</label></th> | ||||
| <td><ul class="errorlist"><li>This field is required.</li></ul> | ||||
| <td><ul class="errorlist" id="id_name_error"><li>This field is required.</li></ul> | ||||
| <input type="text" name="name" id="id_name" aria-invalid="true" required></td></tr> | ||||
| <tr class="required"><th><label class="required" for="id_is_cool">Is cool:</label></th> | ||||
| <td><select name="is_cool" id="id_is_cool"> | ||||
| @@ -3763,14 +3766,14 @@ Options: <select multiple name="options" aria-invalid="true" required> | ||||
| <tr><th><label for="id_email">Email:</label></th><td> | ||||
| <input type="email" name="email" id="id_email" maxlength="320"></td></tr> | ||||
| <tr class="required error"><th><label class="required" for="id_age">Age:</label></th> | ||||
| <td><ul class="errorlist"><li>This field is required.</li></ul> | ||||
| <td><ul class="errorlist" id="id_age_error"><li>This field is required.</li></ul> | ||||
| <input type="number" name="age" id="id_age" aria-invalid="true" required></td></tr>""", | ||||
|         ) | ||||
|         self.assertHTMLEqual( | ||||
|             p.as_div(), | ||||
|             '<div class="required error"><label for="id_name" class="required">Name:' | ||||
|             '</label><ul class="errorlist"><li>This field is required.</li></ul>' | ||||
|             '<input type="text" name="name" required id="id_name" ' | ||||
|             '</label><ul class="errorlist" id="id_name_error"><li>This field is ' | ||||
|             'required.</li></ul><input type="text" name="name" required id="id_name" ' | ||||
|             'aria-invalid="true" /></div>' | ||||
|             '<div class="required"><label for="id_is_cool" class="required">Is cool:' | ||||
|             '</label><select name="is_cool" id="id_is_cool">' | ||||
| @@ -3779,8 +3782,8 @@ Options: <select multiple name="options" aria-invalid="true" required> | ||||
|             '</select></div><div><label for="id_email">Email:</label>' | ||||
|             '<input type="email" name="email" id="id_email" maxlength="320"/></div>' | ||||
|             '<div class="required error"><label for="id_age" class="required">Age:' | ||||
|             '</label><ul class="errorlist"><li>This field is required.</li></ul>' | ||||
|             '<input type="number" name="age" required id="id_age" ' | ||||
|             '</label><ul class="errorlist" id="id_age_error"><li>This field is ' | ||||
|             'required.</li></ul><input type="number" name="age" required id="id_age" ' | ||||
|             'aria-invalid="true" /></div>', | ||||
|         ) | ||||
|  | ||||
| @@ -4255,8 +4258,10 @@ Options: <select multiple name="options" aria-invalid="true" required> | ||||
|  | ||||
|         errors = form.errors.as_ul() | ||||
|         control = [ | ||||
|             '<li>foo<ul class="errorlist"><li>This field is required.</li></ul></li>', | ||||
|             '<li>bar<ul class="errorlist"><li>This field is required.</li></ul></li>', | ||||
|             '<li>foo<ul class="errorlist" id="id_foo_error"><li>This field is required.' | ||||
|             "</li></ul></li>", | ||||
|             '<li>bar<ul class="errorlist" id="id_bar_error"><li>This field is required.' | ||||
|             "</li></ul></li>", | ||||
|             '<li>__all__<ul class="errorlist nonfield"><li>Non-field error.</li></ul>' | ||||
|             "</li>", | ||||
|         ] | ||||
| @@ -4461,7 +4466,8 @@ Options: <select multiple name="options" aria-invalid="true" required> | ||||
|             form.as_ul(), | ||||
|             '<li><ul class="errorlist nonfield">' | ||||
|             "<li>(Hidden field hidden) Foo & "bar"!</li></ul></li>" | ||||
|             '<li><ul class="errorlist"><li>Foo & "bar"!</li></ul>' | ||||
|             '<li><ul class="errorlist" id="id_visible_error"><li>Foo & ' | ||||
|             ""bar"!</li></ul>" | ||||
|             '<label for="id_visible">Visible:</label> ' | ||||
|             '<input type="text" name="visible" aria-invalid="true" value="b" ' | ||||
|             'id="id_visible" required>' | ||||
|   | ||||
| @@ -97,7 +97,7 @@ class FormsI18nTests(SimpleTestCase): | ||||
|             f = SomeForm({}) | ||||
|             self.assertHTMLEqual( | ||||
|                 f.as_p(), | ||||
|                 '<ul class="errorlist"><li>' | ||||
|                 '<ul class="errorlist" id="id_somechoice_error"><li>' | ||||
|                 "\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c" | ||||
|                 "\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n" | ||||
|                 "<p><label>\xc5\xf8\xdf:</label>" | ||||
|   | ||||
| @@ -3207,11 +3207,13 @@ class ModelFormCustomErrorTests(SimpleTestCase): | ||||
|         errors = CustomErrorMessageForm(data).errors | ||||
|         self.assertHTMLEqual( | ||||
|             str(errors["name1"]), | ||||
|             '<ul class="errorlist"><li>Form custom error message.</li></ul>', | ||||
|             '<ul class="errorlist" id="id_name1_error">' | ||||
|             "<li>Form custom error message.</li></ul>", | ||||
|         ) | ||||
|         self.assertHTMLEqual( | ||||
|             str(errors["name2"]), | ||||
|             '<ul class="errorlist"><li>Model custom error message.</li></ul>', | ||||
|             '<ul class="errorlist" id="id_name2_error">' | ||||
|             "<li>Model custom error message.</li></ul>", | ||||
|         ) | ||||
|  | ||||
|     def test_model_clean_error_messages(self): | ||||
| @@ -3220,14 +3222,15 @@ class ModelFormCustomErrorTests(SimpleTestCase): | ||||
|         self.assertFalse(form.is_valid()) | ||||
|         self.assertHTMLEqual( | ||||
|             str(form.errors["name1"]), | ||||
|             '<ul class="errorlist"><li>Model.clean() error messages.</li></ul>', | ||||
|             '<ul class="errorlist" id="id_name1_error">' | ||||
|             "<li>Model.clean() error messages.</li></ul>", | ||||
|         ) | ||||
|         data = {"name1": "FORBIDDEN_VALUE2", "name2": "ABC"} | ||||
|         form = CustomErrorMessageForm(data) | ||||
|         self.assertFalse(form.is_valid()) | ||||
|         self.assertHTMLEqual( | ||||
|             str(form.errors["name1"]), | ||||
|             '<ul class="errorlist">' | ||||
|             '<ul class="errorlist" id="id_name1_error">' | ||||
|             "<li>Model.clean() error messages (simpler syntax).</li></ul>", | ||||
|         ) | ||||
|         data = {"name1": "GLOBAL_ERROR", "name2": "ABC"} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user