#35987: ErrorList.copy() reverts to default renderer
----------------------------------------+------------------------------
               Reporter:  Adam Johnson  |          Owner:  Adam Johnson
                   Type:  Bug           |         Status:  assigned
              Component:  Forms         |        Version:  dev
               Severity:  Normal        |       Keywords:
           Triage Stage:  Unreviewed    |      Has patch:  0
    Needs documentation:  0             |    Needs tests:  0
Patch needs improvement:  0             |  Easy pickings:  0
                  UI/UX:  0             |
----------------------------------------+------------------------------
 When an `ErrorList` is copied, it loses track of any custom renderer,
 reverting to the default one. Practically, this means custom styles are
 not applied to non-field errors when rendering a whole form.

 For example, I wrote a custom renderer that swaps some templates like so:

 {{{#!python
 from django import forms
 from django.forms.renderers import TemplatesSetting

 from django.template.exceptions import TemplateDoesNotExist


 class CustomRenderer(TemplatesSetting):
     def get_template(self, template_name):
         if template_name.startswith("django/forms/"):
             # Load our custom version from "custom/forms/" if it exists
             our_template =
 f"custom/{template_name.removeprefix('django/')}"
             try:
                 return super().get_template(our_template)
             except TemplateDoesNotExist:
                 pass
         return super().get_template(template_name)


 class MyForm(forms.Form):
     default_renderer = CustomRenderer()
 }}}

 The custom error list template uses some CSS utility classes from
 Tailwind, like `text-red-600`:

 {{{#!htmldjango
 {% if errors %}<ul class="text-red-600">{% for error in errors %}<li>{{
 error }}</li>{% endfor %}</ul>{% endif %}
 }}}

 Creating a form with a non-field error and rendering those errors uses the
 custom template:

 {{{#!pycon
 In [1]: from example.forms import MyForm
    ...:
    ...: form = MyForm({})
    ...: form.full_clean()
    ...: form.add_error(None, "Test error")

 In [2]: form.non_field_errors().render()
 Out[2]: '<ul class="text-red-600"><li>Test error</li></ul>'
 }}}

 But rendering the whole form reverts to the default template, from the
 default renderer:

 {{{#!pycon
 In [3]: form.render()
 Out[3]: '<ul class="errorlist nonfield"><li>Test error</li></ul>\n\n
 <div></div>'
 }}}

 This occurs because the `ErrorList` is copied in `Form.get_context()`:

 
https://github.com/django/django/blob/1860a1afc9ac20750f932e8e0a94b32d096f2848/django/forms/forms.py#L225

 The fix would be to also copy over `renderer` in `ErrorList.copy()`:

 
https://github.com/django/django/blob/1860a1afc9ac20750f932e8e0a94b32d096f2848/django/forms/utils.py#L165

 I think this has probably been an issue ever since a custom renderer
 became possible in #31026.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35987>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/01070193aafd7bcc-ad9e7bbd-7d87-4a3b-94fb-e2c3a6fd47a1-000000%40eu-central-1.amazonses.com.

Reply via email to