Ticket #6652: allow_clean_to_specify_the_wrong_field-updated-tests-docs.diff

File allow_clean_to_specify_the_wrong_field-updated-tests-docs.diff, 8.3 KB (added by mrts, 16 years ago)

Appends errors from clean() to original errors.

  • django/newforms/util.py

     
    5050        return repr([force_unicode(e) for e in self])
    5151
    5252class ValidationError(Exception):
    53     def __init__(self, message):
     53    def __init__(self, message, field_name=None):
    5454        """
    5555        ValidationError can be passed any object that can be printed (usually
    5656        a string) or a list of objects.
    5757        """
     58        self.field_name = field_name
    5859        if isinstance(message, list):
    5960            self.messages = ErrorList([smart_unicode(msg) for msg in message])
    6061        else:
  • django/newforms/forms.py

     
    215215        try:
    216216            self.cleaned_data = self.clean()
    217217        except ValidationError, e:
    218             self._errors[NON_FIELD_ERRORS] = e.messages
     218            name = e.field_name or NON_FIELD_ERRORS
     219            if e.field_name and name in self._errors:
     220                self._errors[name] = ErrorList(self._errors[name] + e.messages)
     221            else:
     222                self._errors[name] = e.messages
    219223        if self._errors:
    220224            delattr(self, 'cleaned_data')
    221225
  • tests/regressiontests/forms/forms.py

     
    611611
    612612Another way of doing multiple-field validation is by implementing the
    613613Form's clean() method. If you do this, any ValidationError raised by that
    614 method will not be associated with a particular field; it will have a
    615 special-case association with the field named '__all__'.
     614method either will not be associated with a particular field
     615and will have a special-case association with the field named '__all__';
     616or if you specify the invalid field explicitly by passing the 'field_name'
     617parameter to the error constructor, it will be associated with the field in
     618similar manner to clean_XXX().
    616619Note that in Form.clean(), you have access to self.cleaned_data, a dictionary of
    617620all the fields/values that have *not* raised a ValidationError. Also note
    618621Form.clean() is required to return a dictionary of all clean data.
     
    653656>>> f.cleaned_data
    654657{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
    655658
     659The following is a somewhat contrived example that tests specifying the
     660invalid field in clean(). Note that default values in self.cleaned_data.get()
     661are neccessary to test if errors are appended correctly to base field errors.
     662
     663>>> class EmailRegistration(Form):
     664...    username = CharField(max_length=10)
     665...    email1 = EmailField()
     666...    email2 = EmailField()
     667...    def clean(self):
     668...        email1 = self.cleaned_data.get('email1', 'foo')
     669...        email2 = self.cleaned_data.get('email2', 'bar')
     670...        if email1 != email2:
     671...            raise ValidationError(u'Please make sure the email addresses match.', field_name = 'email2')
     672...        return self.cleaned_data
     673...
     674>>> f = EmailRegistration(auto_id=False)
     675>>> f.errors
     676{}
     677>>> f = EmailRegistration({}, auto_id=False)
     678>>> f.errors
     679{'username': [u'This field is required.'], 'email2': [u'This field is required.', u'Please make sure the email addresses match.'], 'email1': [u'This field is required.']}
     680>>> print f.as_ul()
     681<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li>
     682<li><ul class="errorlist"><li>This field is required.</li></ul>Email1: <input type="text" name="email1" /></li>
     683<li><ul class="errorlist"><li>This field is required.</li><li>Please make sure the email addresses match.</li></ul>Email2: <input type="text" name="email2" /></li>
     684>>> print f.as_table()
     685<tr><th>Username:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" maxlength="10" /></td></tr>
     686<tr><th>Email1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="email1" /></td></tr>
     687<tr><th>Email2:</th><td><ul class="errorlist"><li>This field is required.</li><li>Please make sure the email addresses match.</li></ul><input type="text" name="email2" /></td></tr>
     688>>> f = EmailRegistration({'username': 'adrian', 'email1': 'foo@foo.com', 'email2': 'bar'}, auto_id=False)
     689>>> f.errors
     690{'email2': [u'Enter a valid e-mail address.', u'Please make sure the email addresses match.']}
     691>>> f.as_ul()
     692u'<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>\n<li>Email1: <input type="text" name="email1" value="foo@foo.com" /></li>\n<li><ul class="errorlist"><li>Enter a valid e-mail address.</li><li>Please make sure the email addresses match.</li></ul>Email2: <input type="text" name="email2" value="bar" /></li>'
     693>>> f.as_table()
     694u'<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>\n<tr><th>Email1:</th><td><input type="text" name="email1" value="foo@foo.com" /></td></tr>\n<tr><th>Email2:</th><td><ul class="errorlist"><li>Enter a valid e-mail address.</li><li>Please make sure the email addresses match.</li></ul><input type="text" name="email2" value="bar" /></td></tr>'
     695>>> f = EmailRegistration({'username': 'adrian', 'email1': 'foo@foo.com', 'email2': 'bar@bar.com'}, auto_id=False)
     696>>> f.errors
     697{'email2': [u'Please make sure the email addresses match.']}
     698>>> f.as_ul()
     699u'<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>\n<li>Email1: <input type="text" name="email1" value="foo@foo.com" /></li>\n<li><ul class="errorlist"><li>Please make sure the email addresses match.</li></ul>Email2: <input type="text" name="email2" value="bar@bar.com" /></li>'
     700>>> f.as_table()
     701u'<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>\n<tr><th>Email1:</th><td><input type="text" name="email1" value="foo@foo.com" /></td></tr>\n<tr><th>Email2:</th><td><ul class="errorlist"><li>Please make sure the email addresses match.</li></ul><input type="text" name="email2" value="bar@bar.com" /></td></tr>'
     702>>> f = EmailRegistration({'username': 'adrian', 'email1': 'foo@foo.com', 'email2': 'foo@foo.com'}, auto_id=False)
     703>>> f.errors
     704{}
     705>>> f.as_ul()
     706u'<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>\n<li>Email1: <input type="text" name="email1" value="foo@foo.com" /></li>\n<li>Email2: <input type="text" name="email2" value="foo@foo.com" /></li>'
     707>>> f.as_table()
     708u'<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>\n<tr><th>Email1:</th><td><input type="text" name="email1" value="foo@foo.com" /></td></tr>\n<tr><th>Email2:</th><td><input type="text" name="email2" value="foo@foo.com" /></td></tr>'
     709>>> f.cleaned_data
     710{'username': u'adrian', 'email2': u'foo@foo.com', 'email1': u'foo@foo.com'}
     711
    656712# Dynamic construction ########################################################
    657713
    658714It's possible to construct a Form dynamically by adding to the self.fields
  • docs/newforms.txt

     
    15891589      cleaned data if you override this method (by default, ``Form.clean()``
    15901590      just returns ``self.cleaned_data``).
    15911591
    1592       Note that any errors raised by your ``Form.clean()`` override will not
    1593       be associated with any field in particular. They go into a special
     1592      Note that by default any errors raised by your ``Form.clean()`` override
     1593      will not be associated with any field in particular. They go into a special
    15941594      "field" (called ``__all__``), which you can access via the
    1595       ``non_field_errors()`` method if you need to.
     1595      ``non_field_errors()`` method if you need to. However, if you want to
     1596      explicitly associate the error with a particular field, you can do so
     1597      by passing the ``field_name`` parameter to the ``ValidationError``
     1598      constructor.
    15961599
    15971600These methods are run in the order given above, one field at a time.  That is,
    15981601for each field in the form (in the order they are declared in the form
Back to Top