[Fixed]-Render form errors with the label rather than field name

13👍

I don’t see a simple way to do this.

The errors attribute of the form actually returns an ErrorDict, a class defined in django.forms.utils – it’s a subclass of dict that knows to produce that ul rendering of itself as its unicode representation. But the keys are actually the field names, and that’s important to maintain for other behavior. So it provides no easy access to the field labels.

You could define a custom template tag that accepts the form to produce the rendering you prefer, since in Python code it’s easy to get the field label given the form and the field name. Or you could construct an error list by label in the view, add it to your context, and use that instead.

edit
Alternately again, you can iterate over the fields and check their individual errors, remembering to display non_field_errors as well. Something like:

<ul class="errorlist">
  {% if form.non_field_errors %}
    <li>{{ form.non_field_errors }}</li>
  {% endif %}
  {% for field in form %}
    {% if field.errors %}
      <li>
        {{ field.label }}
        <ul class="errorlist">
          {% for error in field.errors %}
            <li>{{ error }}</li>
          {% endfor %}
        </ul>
      </li>
    {% endif %}
  {% endfor %}
</ul>

You might want to wrap non_field_errors in a list as well, depending.

9👍

I know this has already been answered but, I ran across the same scenario and found there is a simple way to use the label:

{% if form.errors %}
    <ul class="user-msg error">
    {% for field in form %}
        {% for error in field.errors %}
            <li>
              {% if field != '__all__' %}
                <strong>{{ field.label }}:</strong>
              {% endif %}
              {{ error }}
            </li>
        {% endfor %}
    {% endfor %}
    </ul>
{% endif %}

2👍

I solved this in a custom form class which all my forms inherit instead of django.forms.Form. There I change the way form.errors works by returning a custom ErrorDict whose as_ul method takes labels into account. Thus you don’t need to change your templates, but you need to have your forms inherit CustomBaseForm.

class CustomErrorDict(ErrorDict):
    def __init__(self, form, iterable=None, **kwargs):
        self.form = form
        super(CustomErrorDict, self).__init__(iterable, **kwargs)

    def as_ul(self):
        if not self:
            return u''

        def humanify(field_name):
            try:
                return self.form.fields[field_name].label or field_name
            except:
                return field_name

        # main logic is copied from the original ErrorDict:
        return mark_safe(u'<ul class="errorlist">%s</ul>'
                % ''.join([u'<li>%s%s</li>' % (humanify(k), force_unicode(v))
                    for k, v in self.items()]))

class CustomBaseForm(forms.Form):
    @property
    def errors(self):
        return CustomErrorDict(self, super(forms.Form, self).errors)

    ... rest of CustomBaseForm ...

2👍

from django import forms

def my_clean(self):
    self.my_errors = ''
    for x in self.visible_fields():
        if x.errors:
            self.my_errors += "<p>%s: %s</p>" % (x.label, x.errors)


class SetPwdForm(forms.Form):
    pwd= forms.CharField(label='password', required=True, min_length=6)
    def clean(self):
        ...
        my_clean(self)

use myform.my_errors in views.

👤duma

0👍

Just in case anyone is looking do something like this using the django.contrib.messages framework in, for example, a FormView:

def form_invalid(self, form):
    for field, errors in form.errors.items():
        for error in errors:
            messages.error(
                self.request,
                form.fields[field].label + ": " + error
            )

Note this is just a basic template, you’ll have to take care of non-field errors et cetera in your code in the case where form.fields[field] doesn’t make sense.

👤Escher

0👍

The following approach shows verbose_name instead of the field name.
It can be used in get_context_data() too but personally, I prefer this way:

from django.core.exceptions import FieldDoesNotExist


class ShowVerboseNameInFormsMixin:

    def add_error(self, field, error):
        super(ShowVerboseNameInFormsMixin, self).add_error(field, error)
        for field, message in self._errors.copy().items():
            try:
                verbose_name = self._meta.model._meta.get_field(field).verbose_name
                del self._errors[field]
                self._errors[verbose_name] = self.error_class()
                self._errors[verbose_name].extend(message)
            except FieldDoesNotExist:
                pass

and then use it like this:

from django import forms


class FooForm(ShowVerboseNameInFormsMixin, forms.ModelForm):
    class Meta:
        model = Foo
        fields = ['foo', 'bar', 'baz']

with a little extra code, It can show the __all__ to all or any other intended string.

-1👍

Here’s the filter I used to render an error list with the field label, following Peter’s suggestion.

from django.utils.safestring import mark_safe
from django.template import Library, Context, loader
register = Library()

@register.filter
def form_error_list(form):

    result = ""    
    if form.errors:
        fields = form.fields
        error_items = []
        for error_field in form.errors:
            label = fields[error_field].label
            if not label:
                label = ' '.join([word.capitalize() for word in error_field.split('_')])
            errors = form.errors[error_field]
            error_li = ''.join(['<li>{0}</li>'.format(error) for error in errors])
            error_items.append({'label': label, 'error_li': error_li})    
        inner = ''.join(['<li>{0}<ul class="errorlist">{1}</ul></li>'.format(item['label'], item['error_li']) for item in error_items])
        result = mark_safe('<ul class="errorlist">{0}</ul>'.format(inner))

    return result

Leave a comment