Django's forms feature a way to customize how errors are displayed by allowing one to subclass django.forms.util.ErrorList?. However, there seems to be something missing in there to make it work completely. Let me summarize what I found :
1. When django.forms.Form gathers cleaned data in full_clean(), it sets keyed data on its internal _errors variable like so :
except ValidationError, e:
self._errors[name] = e.messages
2. That e.messages data comes from ValidationError Exception subclass :
class ValidationError(Exception):
def __init__(self, message):
"""
ValidationError can be passed any object that can be printed (usually
a string) or a list of objects.
"""
if isinstance(message, list):
self.messages = ErrorList([smart_unicode(msg) for msg in message])
else:
message = smart_unicode(message)
self.messages = ErrorList([message])
3. When calling as_ul(), as_p(), etc. Form does the following :
bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable.
There, it does the right thing, i.e. wrap errors in the specified ErrorList subclass provided by the user, here available through self.error_class. As you can see though, this is not done in the ValidationError? class. It just takes for granted that in should be wrapped in the original ErrorList class. Perfectly understandable since it doesn't know anything about the Form instance, nor any of its attribute. A very simple fix to this is to replace the line in (1):
# self._errors[name] = e.messages (before)
self._errors[name] = self.error_class(e.messages)
So I just attached a patch doing just that and adding some tests to make sure such customization works perfectly.