1

I'm using Django 1.11. I have a form with validation methods, which correctly raise ValidationErrors. When a form is submitted invalid, form.error_messages get filled as expected. However when I want to display errors in template, both form.non_field_errors and form.[field].errors are all blank. Do I need to do anything extra to be able to use these? I'm kinda confused because this always "just worked" for me. I've read Django documentation on forms through and through as well as related SO questions and nothing helped. Thanks for any advice.

PS. I know I could use Django-included PasswordChangeForm and a respective view. I have my reasons that are not obvious from the code below. Thanks.

forms.py

class PasswordChangeForm(forms.Form):
    prefix = 'password_change'
    error_messages = {
        'password_mismatch': "The two password fields didn't match.",
        'password_incorrect': "Your old password was entered incorrectly. Please enter it again.",
    }
    old_password = forms.CharField(
        label="Old password",
        strip=False,
    )
    new_password1 = forms.CharField(
        label="New password",
        strip=False,
        help_text=password_validation.password_validators_help_text_html(),
    )
    new_password2 = forms.CharField(
        label="New password repeated",
        strip=False,
    )

    field_order = ['old_password', 'new_password1', 'new_password2']

    def __init__(self, user, *args, **kwargs):
        self.user = user
        super().__init__(*args, **kwargs)

    def clean_old_password(self):
        """
        Validate that the old_password field is correct.
        """
        old_password = self.cleaned_data["old_password"]
        if not self.user.check_password(old_password):
            raise forms.ValidationError(
                self.error_messages['password_incorrect'],
                code='password_incorrect',
            )
        return old_password

    def clean_new_password2(self):
        password1 = self.cleaned_data.get('new_password1')
        password2 = self.cleaned_data.get('new_password2')
        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

    def save(self, commit=True):
        password = self.cleaned_data["new_password1"]
        self.user.set_password(password)
        if commit:
            self.user.save()
        return self.user

views.py

def profile(request):
    if request.method == 'POST' and 'password_change-submit' in request.POST:
        password_change_form = PasswordChangeForm(request.POST)
        if password_change_form.is_valid():
            pass
        else:
            # for debugging purposes ... this prints errors to the console as expected
            print(password_change_form.error_messages)
    else:
        password_change_form = PasswordChangeForm(request.user)

    context = {'password_change_form': password_change_form}

    return render(request, 'profile.html', context)

template

<form action="{% url 'profile' %}" method="POST">
    {% csrf_token %}
    {{ password_change_form.non_field_errors }}
    {{ password_change_form.error_messages }}  <-- for debugging purposes, this actually shows something
    <div class="form-group">
        {{ password_change_form.old_password.errors }}
        <label for="{{ password_change_form.old_password.id_for_label }}">{{ password_change_form.old_password.label }}</label>
        <input type="password" class="form-control" id="{{ password_change_form.old_password.id_for_label }}" name="{{ 
password_change_form.old_password.html_name }}" placeholder="{{ password_change_form.old_password.label }}">
    </div>
    <div class="form-group">
        {{ password_change_form.new_password1.errors }}
        <label for="{{ password_change_form.new_password1.id_for_label }}">{{ password_change_form.new_password1.label }}</label>
        <input type="password" class="form-control" id="{{ password_change_form.new_password1.id_for_label }}" name="{{ 
password_change_form.new_password1.html_name }}" placeholder="{{ password_change_form.new_password1.label }}">
    </div>
    <div class="form-group">
        {{ password_change_form.new_password2.errors }}
        <label for="{{ password_change_form.new_password2.id_for_label }}">{{ password_change_form.new_password2.label }}</label>
        <input type="password" class="form-control" id="{{ password_change_form.new_password2.id_for_label }}" name="{{ 
password_change_form.new_password2.html_name }}" placeholder="{{ password_change_form.new_password2.label }}">
    </div>
    <input type="submit" class="btn btn-primary" name="password_change-submit" value="Change password">
</form>

I would expect that {{ password_change_form.non_field_errors }} and {{ password_change_form.[field].errors }} will show something in the template, however they are blank. {{ password_change_form.error_messages }} shows the errors (as it does in a view too), but I actually never used this in a template and I guess it shouldn't be used this way.

1
  • You're right, I delete my comment. Sorry for that. Commented May 21, 2017 at 9:11

1 Answer 1

2

Figured it out. Stupid mistake :)

On the third row of views.py, I'm passing the data incorrectly. It should be done like this:

password_change_form = PasswordChangeForm(user=request.user, data=request.POST)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.