Django

Code

Ticket #10968 (closed: fixed)

Opened 9 months ago

Last modified 5 months ago

Appears like there's something missing when subclassing django.forms' ErrorList

Reported by: matehat Assigned to: lukeplant
Milestone: Component: Forms
Version: 1.0 Keywords:
Cc: mathieu@matehat.com Triage Stage: Unreviewed
Has patch: 1 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 0

Description

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.

Attachments

patch.diff (2.4 kB) - added by matehat on 04/30/09 15:34:06.
Patch for django.forms.forms.py and regressiontests

Change History

04/30/09 15:34:06 changed by matehat

  • attachment patch.diff added.

Patch for django.forms.forms.py and regressiontests

04/30/09 15:36:31 changed by matehat

  • cc set to mathieu@matehat.com.
  • needs_better_patch changed.
  • has_patch set to 1.
  • needs_tests changed.
  • needs_docs changed.

04/30/09 15:37:44 changed by matehat

  • owner changed from nobody to matehat.

07/24/09 15:50:25 changed by toszter@gmail.com

I second this solution after finding and reproducing the same error on my own. Nice fix.

09/11/09 05:45:42 changed by lukeplant

  • owner changed from matehat to lukeplant.
  • status changed from new to assigned.

The patch misses using error_class for the __all__ error, and the tests rely on dictionary key order. I've fixed these things, and will apply soon, thanks.

09/11/09 05:47:40 changed by lukeplant

  • status changed from assigned to closed.
  • resolution set to fixed.

(In [11498]) Fixed #10968 - Form.errors should use Form.error_class. Thanks for report and initial patch, matehat.


Add/Change #10968 (Appears like there's something missing when subclassing django.forms' ErrorList)




Change Properties
Action