[Fixed]-Django clean method throwing KeyError on POST

12👍

Aha! The validation error message that you are seeing is actually not a validation error message. Let me explain. When you render the model form instance using as_p, it renders each field in the following way:

<p><label ...>fieldname</label> <input ... name="fieldname" /> HELP TEXT IF ANY</p> 

The string Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters that you are seeing to the right hand side of the field is nothing but the help text. This help text is taken from the model definition – you can verify this by visiting django/contrib/auth/models.py and inspecting the definition of User class.

When you override the username field you are omitting any help text. Naturally the “error” disappears.

In order to verify this theory do the following in your main method.

def main(request):
    uf = forms.UserForm()
    upf = forms.UserProfileForm()
    print "User form is bound:%s errors:%s" % (uf.is_bound, uf.errors)
    return render_to_response("authentication/index.html", {'form1': uf, 'form2':upf})

Update

if self.cleaned_data['cpassword']!=self.cleaned_data['password']:

This line can cause trouble when the user doesn’t supply one or both of password and cpassword. For example, try this from the Django shell:

>>> data = dict(username = 'admin', cpassword = 'foo', email='admin@bar.com')
>>> f = RegisterForm(data)
>>> f.is_bound
True
>>> f.is_valid()

Traceback (most recent call last):
  ...
  File "<pyshell#2>", line 8, in clean
    if self.cleaned_data['cpassword']!=self.cleaned_data['password']:
KeyError: 'password'

Change your form’s clean method to make sure that both values are present before comparing. Something like this:

def clean(self):
    password = self.cleaned_data.get('password', None)
    cpassword = self.cleaned_data.get('cpassword', None)
    if password and cpassword and (password == cpassword):
        return self.cleaned_data
    else:
        raise forms.ValidationError("Passwords don't match")

22👍

From my own experience, I found that if you want to do some validation in multiple fields, even if they are marked as required=True, when you override the clean() method in your form, if the fields you want to validate are not filled when submitting it and you try to acess them as cleaned_data["field_name"] your code will explode with a KeyError. To avoid this, just access the field in cleaned_data through the get() dictionary method and check if is None, or pass a default value. As a corollary:

my_field = cleaned_data.get("field_name")  # This is safe and it will work! :)
my_field = cleaned_data["field_name"]  # This will crash when the field was not filled! :(

I hope this helps someone else, I lost a great amount of time because of this silly thing!

Leave a comment