Django自定义密码修改表单在GET请求时触发验证的问题
问题根源
- GET请求错误传递POST数据:
ChangePasswordView的get方法里创建表单时传了request.POST——但GET请求的request.POST是空字典,相当于给表单传了空数据,直接触发了验证逻辑。 - clean方法未处理空值场景:
clean方法最后直接调用password_validation.validate_password(password1, self.user),当password1为空(比如GET请求时),这个验证会强制抛出“密码不能为空”的错误。 - 父类初始化方法不能删:
super().__init__(*args, **kwargs)是Django表单初始化的核心步骤,负责设置_errors等必要属性,删掉必然导致表单功能异常。
修复方案
1. 修正视图的GET方法
GET请求要创建空表单,不要传request.POST;同时POST验证失败时,要返回表单页面显示错误,不能直接重定向:
class ChangePasswordView(LoginRequiredMixin, View): def get(self, request, *args, **kwargs): # 创建空表单,不传request.POST form = ChangePasswordForm(request.user) context = {'form': form} return render(request, 'auth/change_password.html', context) def post(self, request, *args, **kwargs): form = ChangePasswordForm(request.user, request.POST) if form.is_valid(): user = form.save() update_session_auth_hash(request, user) messages.success(request, "密码更新成功") return redirect('custom_redirect') # 验证失败时返回原页面,带错误表单 return render(request, 'auth/change_password.html', {'form': form})
2. 完善表单的clean方法
在调用密码验证前先判断字段是否有值,同时把错误绑定到对应字段,提升错误提示的精准度:
from django.core.exceptions import ValidationError from django.contrib.auth import password_validation class ChangePasswordForm(forms.Form): old_password = forms.CharField(max_length=50, widget=forms.PasswordInput) password1 = forms.CharField(max_length=50, widget=forms.PasswordInput) password2 = forms.CharField(max_length=50, widget=forms.PasswordInput) def __init__(self, user, *args, **kwargs): self.user = user super().__init__(*args, **kwargs) def clean(self): cleaned_data = super().clean() old_password = cleaned_data.get('old_password') password1 = cleaned_data.get('password1') password2 = cleaned_data.get('password2') # 仅当所有字段都有值时才执行验证 if old_password and password1 and password2: if not self.user.check_password(old_password): self.add_error('old_password', "旧密码无效,请输入当前密码") elif password1 != password2: self.add_error('password2', "两次输入的密码不一致,请重新输入") # 捕获密码规则验证错误,绑定到新密码字段 try: password_validation.validate_password(password1, self.user) except ValidationError as e: self.add_error('password1', e) return cleaned_data def save(self, commit=True): new_password = self.cleaned_data.get('password2') self.user.set_password(new_password) if commit: self.user.save() return self.user
这里给字段加了widget=forms.PasswordInput,避免密码明文显示;用self.add_error()替代直接抛异常,让错误提示和对应字段绑定。
3. 模板优化(可选)
如果想利用表单的错误提示机制,可以把手动写的input改成表单渲染方式,让错误提示更精准:
<form class="card-style mb-30" action="{% url 'account_change_password' %}" method="post"> {% csrf_token %} <h6 class="mb-25">修改密码</h6> {% if form.non_field_errors %} <div class="text-sm mb-25 alert alert-danger "> <i class="lni lni-close"></i> <span>{{form.non_field_errors}}</span> </div> {% endif %} <div class="input-style-3"> {{ form.old_password }} {% if form.old_password.errors %} <div class="text-sm text-danger">{{ form.old_password.errors.0 }}</div> {% endif %} <span class="icon"><i class="lni lni-key"></i></span> </div> <div class="input-style-3"> {{ form.password1 }} {% if form.password1.errors %} <div class="text-sm text-danger">{{ form.password1.errors.0 }}</div> {% endif %} <span class="icon"><i class="lni lni-key"></i></span> </div> <div class="input-style-3"> {{ form.password2 }} {% if form.password2.errors %} <div class="text-sm text-danger">{{ form.password2.errors.0 }}</div> {% endif %} <span class="icon"><i class="lni lni-key"></i></span> </div> <div class="col-12"> <div class="button-group d-flex justify-content-center flex-wrap"> <button type="submit" class="main-btn primary-btn btn-hover m-2"> 修改密码 </button> </div> </div> </form>
内容的提问来源于stack exchange,提问作者Ruby
相关产品推荐
相关产品推荐

