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.