You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django自定义密码修改表单在GET请求时触发验证的问题

问题根源

  1. GET请求错误传递POST数据:ChangePasswordView的get方法里创建表单时传了request.POST——但GET请求的request.POST是空字典,相当于给表单传了空数据,直接触发了验证逻辑。
  2. clean方法未处理空值场景:clean方法最后直接调用password_validation.validate_password(password1, self.user),当password1为空(比如GET请求时),这个验证会强制抛出“密码不能为空”的错误。
  3. 父类初始化方法不能删: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 00:17:02