Nanor
Nanor

Reputation: 2550

Program runs fine when debugging, but crashes when running ordinarily

I'm using Django forms to change a user's password when they forget it:

#views.py
def change_forgotten_password(request, key):
    if request.method == 'GET':
        form = ChangePasswordForm()
        return render(request, 'freelancestudent/change_forgotten_password.html', {'form': form})
    else:  # post
        form = ChangePasswordForm(request.POST)
        if form.is_valid():
            user = User.objects.get(forgot_password_key=key)
            user.set_password(form.cleaned_data['password'])
            user.save()
            return redirect('/')

#forms.py
class ChangePasswordForm(forms.Form):
    password = forms.CharField(widget=forms.PasswordInput)
    confirm_password = forms.CharField(widget=forms.PasswordInput)

    def is_valid(self):
        if self.cleaned_data['password'] == self.cleaned_data['confirm_password']:
            return True

When I step through my code using PyCharm's debugger everything works exactly as I want it to; if the passwords match it updates the selected user's password. However, if I run it as (python manage.py runserver) it presents the error:

AttributeError at /change-forgotten-password/wemnj8vvk37yvikf/

'ChangePasswordForm' object has no attribute 'cleaned_data' 

at the line in forms.py which reads:

if self.cleaned_data['password'] == self.cleaned_data['confirm_password']:
    ...

Upvotes: 1

Views: 46

Answers (1)

dhke
dhke

Reputation: 15398

form.cleaned_data is filled by form.is_valid(). You forgot to call the super implementation in your form.

def is_valid(self):
    return (
       super(ChangePasswordForm, self).is_valid()
       and self.cleaned_data['password'] == self.cleaned_data['confirm_password']
    )

In general, however, I'd discourage overriding is_valid() and instead override form.clean() or similiar. As indicated by Brandon, django already does this in its own SetPasswordForm:

def clean_new_password2(self):
    password1 = self.cleaned_data.get('password')
    password2 = self.cleaned_data.get('confirm_password')
    if password1 and password2:
        if password1 != password2:
            raise forms.ValidationError(
                self.error_messages['password_mismatch'],
                code='password_mismatch',
            )
    password_validation.validate_password(password2, self.user)
    return password2

You might go DRY and simply re-use the existing ChangePasswordForm, which accepts fields 'old_password', 'new_password1', and 'new_password2' so you might need to adapt your template.

Upvotes: 1

Related Questions