Skip to main content

Error Handling and Presentation

Validation errors in this codebase are managed through specialized collection classes that handle both the storage of error data and its presentation across different formats (HTML, text, and JSON). The system centers around ErrorList and ErrorDict in django/forms/utils.py, which utilize a mixin-based rendering architecture.

The Error Rendering Pipeline

The RenderableErrorMixin provides a consistent interface for converting error collections into user-facing formats. It leverages Django's template engine to decouple the error logic from the presentation layer.

Key methods provided by the mixin include:

  • as_ul(): Renders errors using the template_name_ul template.
  • as_text(): Renders errors using the template_name_text template.
  • as_json(): Serializes error data to a JSON string via get_json_data().

Both ErrorList and ErrorDict implement this mixin and define specific template paths for their default output.

Error Collections

ErrorList

ErrorList is a list-like collection (inheriting from UserList and list) used to store errors for a single field or non-field errors.

  • Dual Inheritance: It inherits from both UserList and list for backward compatibility. To prevent duplicate entries during serialization (like pickling), it overrides __reduce_ex__ to nullify the list iterator.
  • String Conversion: When iterating over an ErrorList, it automatically extracts the message string from ValidationError instances via __getitem__.
  • CSS Classes: The constructor accepts an error_class argument, which defaults to "errorlist". If a custom class is provided, it is appended (e.g., "errorlist my-custom-class").

ErrorDict

ErrorDict is a dictionary subclass where keys are field names and values are ErrorList instances. It is primarily used by BaseForm to store all validation errors discovered during the full_clean() process.

# django/forms/forms.py:328
def full_clean(self):
self._errors = ErrorDict(renderer=self.renderer)
# ... validation logic populates self._errors

Customizing Error Presentation

There are two primary ways to customize how errors appear to users: subclassing the error collections or overriding the default templates.

Subclassing ErrorList

You can create a custom ErrorList to change the HTML structure entirely. For example, to wrap errors in <div> tags instead of the default <ul>, you can define a custom class and pass it to the form's constructor.

from django import forms
from django.utils.safestring import mark_safe

class DivErrorList(forms.utils.ErrorList):
def __str__(self):
return self.as_divs()

def as_divs(self):
if not self:
return ""
return mark_safe(
'<div class="error">%s</div>'
% "".join("<p>%s</p>" % e for e in self)
)

# Usage in a form instance
form = MyForm(data, error_class=DivErrorList)

The BaseForm in django/forms/forms.py stores this class in self.error_class and uses it whenever new error collections are instantiated (e.g., in add_error() or non_field_errors()).

Template Overriding

The default rendering behavior is controlled by templates defined in django/forms/utils.py. You can override these in your project's template directory to change the global appearance of errors:

  • List Templates:
    • django/forms/errors/list/default.html
    • django/forms/errors/list/ul.html
    • django/forms/errors/list/text.txt
  • Dictionary Templates:
    • django/forms/errors/dict/default.html
    • django/forms/errors/dict/ul.html
    • django/forms/errors/dict/text.txt

Programmatic and API Usage

For AJAX requests or API integrations, the error classes provide structured data accessors.

JSON Serialization

The as_json() method returns a JSON string. By default, it does not escape HTML, but you can enable it by passing escape_html=True.

# Example of JSON output structure from ErrorList.get_json_data()
# [{"message": "This field is required.", "code": "required"}]
errors_json = form.errors.as_json()

Raw Data Access

If you need to inspect the underlying ValidationError objects (including their parameters and codes), use as_data():

  • ErrorList.as_data(): Returns a list of ValidationError instances.
  • ErrorDict.as_data(): Returns a dictionary mapping field names to lists of ValidationError instances.

This is useful when you need to perform logic based on specific error codes rather than just displaying the message.

# django/forms/forms.py:318
def has_error(self, field, code=None):
return field in self.errors and (
code is None
or any(error.code == code for error in self.errors.as_data()[field])
)

Integration with Forms

The BaseForm class manages the lifecycle of these error collections. When form.errors is accessed, it triggers full_clean(), which initializes an ErrorDict.

Specific field errors are associated with their HTML id via the field_id attribute in ErrorList, allowing for accessible error reporting (e.g., linking an error message to its input field). Non-field errors are specifically tagged with the "nonfield" CSS class via the non_field_errors() method.