views:

175

answers:

1

I'm getting a KeyError for 'password' when I try to submit my form.

trace:

Request Method: POST
Request URL: http://localhost:8000/register/
Django Version: 1.2.1
Python Version: 2.7.0
Installed Applications:
['django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'djangoproject1.authentication']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware')


Traceback:
File "C:\Python27\lib\site-packages\django\core\handlers\base.py" in get_response
  100.                     response = callback(request, *callback_args, **callback_kwargs)
File "C:\Users\jec23\My Java Projects\djangoproject1\src\djangoproject1\authentication\views.py" in register
  20.         if rf.is_valid() and pf.is_valid():
File "C:\Python27\lib\site-packages\django\forms\forms.py" in is_valid
  121.         return self.is_bound and not bool(self.errors)
File "C:\Python27\lib\site-packages\django\forms\forms.py" in _get_errors
  112.             self.full_clean()
File "C:\Python27\lib\site-packages\django\forms\forms.py" in full_clean
  268.         self._clean_form()
File "C:\Python27\lib\site-packages\django\forms\forms.py" in _clean_form
  296.             self.cleaned_data = self.clean()
File "C:\Users\jec23\My Java Projects\djangoproject1\src\djangoproject1\authentication\forms.py" in clean
  16.         if self.cleaned_data['cpassword']!=self.cleaned_data['password']:

Exception Type: KeyError at /register/
Exception Value: 'password'

views:

def register(request):
    if request.method == 'POST':
        rf = forms.RegisterForm(request.POST)
        pf = forms.ProfileForm(request.POST)
        if rf.is_valid() and pf.is_valid():
            newuser = User(username=rf.cleaned_data['username'],email=rf.cleaned_data['email'])
            newuser.set_password(rf.cleaned_data['password'])
            newuser.save()
            profile = pf.save(commit=False)
            profile.user = newuser
            profile.save()
            return HttpResponseRedirect("/register-success/")
        else:
            return render_to_response("authentication/index.html", {'form1': rf, 'form2':pf})
    else:
        return main(request)

forms:

class RegisterForm(forms.Form):
    username = forms.CharField(min_length=6,max_length=15)
    password = forms.CharField(min_length=6,max_length=15,widget = forms.PasswordInput())
    cpassword = forms.CharField(label='Confirm Password',widget = forms.PasswordInput())
    email = forms.EmailField(label='E-mail Address')

    def clean(self):
        if self.cleaned_data['cpassword']!=self.cleaned_data['password']:
            raise forms.ValidationError("Passwords don't match")
        return self.cleaned_data

class ProfileForm(forms.ModelForm):
    phonenumber = forms.CharField(label='Phone Number')

    class Meta:
        model = UserProfile
        exclude = ('user')
+1  A: 

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='[email protected]')
>>> 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")
Manoj Govindan
Ah! That makes a ton of sense. I would have never figured that out. I got kinda annoyed with the ModelForm for user because I had to redefine fields and I was still getting that KeyError for some reason. I ended up using a regular form and just creating the User.
JPC
Is it possible to make the helptext go away without overriding?
JPC
@JPC: You can override the `__init__` method of the form and set `self.fields['username'].help_text = None`. A bit crufty IMHO. Alternately you can use another method to display the form where you ignore the help text.
Manoj Govindan
What about the KeyError? I'll paste the stacktrace
JPC
stacktrace pasted
JPC
@JPC: Updated answer. See above.
Manoj Govindan
I'll give that a shot, however, from messing around with print statements, even when I put something in both textfields, the "password" field doesn't have a key.
JPC
What's the difference between calling .get on cleaned_data and just subscripting it? I tried it your way and it worked, but if I use subscripts it still gives a key error, even if I do put values in for both fields
JPC
@JPC: Subscripting raises `KeyError` if the key is not found. `get` returns `None` or the optional second argument (which is also `None` in this case) if the key is not found. You can verify this with any dictionary object.
Manoj Govindan
thanks, that makes sense!
JPC
Still not quite working. If you have a clean(self) method, it can access anything in cleaned_data, but if you have a clean_something(self) method, it can only access something. Is that correct? That's the behavior that I'm seeing. I can get it to work if I use clean but if I use clean_password, then cpassword is always None
JPC
If your validation logic requires _multiple fields_ then `clean` is the one you should be using rather than `clean_fieldname`. The value of `cpassword` will always be `None` inside `clean_password`. This is expected as `clean_cpassword` hasn't been called yet and hence there is no value for cpassword.
Manoj Govindan
perfect, thanks
JPC