Opened 9 days ago

Closed 8 days ago

Last modified 8 days ago

#35987 closed Bug (fixed)

ErrorList.copy() reverts to default renderer

Reported by: Adam Johnson Owned by: Adam Johnson
Component: Forms Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

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:

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:

{% 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:

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:

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.

Change History (5)

comment:1 by Adam Johnson, 9 days ago

Has patch: set

comment:2 by Sarah Boyce, 9 days ago

Triage Stage: UnreviewedAccepted

Thank you!

comment:3 by Sarah Boyce, 9 days ago

Triage Stage: AcceptedReady for checkin

comment:4 by Sarah Boyce <42296566+sarahboyce@…>, 8 days ago

Resolution: fixed
Status: assignedclosed

In 4806c42:

Fixed #35987 -- Made ErrorList.copy() copy the renderer attribute.

comment:5 by Sarah Boyce <42296566+sarahboyce@…>, 8 days ago

In 5e998d71:

Refs #35987 -- Added extra tests for ErrorList and ErrorDict copy methods.

Note: See TracTickets for help on using tickets.
Back to Top