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

使用django-extra-views的UpdateWithInlinesView更新用户密码异常

解决Django-extra-views中密码更新被父类方法覆盖的问题

这个问题我之前也踩过坑,核心原因是你在forms_valid里手动更新了用户密码,但父类的forms_valid方法会重新保存表单绑定的Users实例——而你的AdminProfileForm并没有把密码相关字段纳入配置,表单保存时会用实例原有数据覆盖你刚设置的新密码。

问题根源拆解

你当前的代码逻辑存在一个关键漏洞:

  1. 手动获取self.request.user设置新密码并保存
  2. 调用父类forms_valid,它会处理AdminProfileForm对应的Users实例
  3. 由于表单只包含first_name和last_name,保存时会忽略你手动修改的密码,用实例原本的旧密码覆盖回去

最优解决方案:在表单的clean方法中处理密码

把密码验证和设置逻辑移到表单的clean方法里,这样在表单验证阶段就更新好实例的密码,父类保存时自然会用更新后的实例:

修改表单定义

class AdminProfileForm(forms.ModelForm): 
    class Meta: 
        model = Users 
        fields = ['first_name', 'last_name'] 
    # 设置为非必填,允许用户不修改密码
    password1 = forms.CharField(widget=forms.PasswordInput, label='New Password', required=False) 
    password2 = forms.CharField(widget=forms.PasswordInput, label='Confirm Password', required=False)

    def clean(self):
        cleaned_data = super().clean()
        password1 = cleaned_data.get('password1')
        password2 = cleaned_data.get('password2')

        # 仅当用户输入新密码时才执行验证逻辑
        if password1 or password2:
            if password1 != password2:
                raise forms.ValidationError("Passwords do not match.")
            # 直接在表单绑定的instance上设置密码
            self.instance.set_password(password1)
        return cleaned_data

简化视图的forms_valid方法

现在不需要在视图里手动处理密码了,父类方法会自动保存已经更新好密码的实例:

class AdminProfile(BaseContext, UpdateWithInlinesView): 
    # 你的其他配置(比如template_name等)
    inlines = [AdminInline] 
    form_class = AdminProfileForm 
    
    def get_object(self): 
        return Users.objects.get(pk=self.request.user.id) 
    
    def forms_valid(self, form, inlines):
        # 父类会自动保存包含新密码的form.instance
        return super().forms_valid(form, inlines)

另一种思路:在视图中直接更新表单实例

如果你不想修改表单,也可以在视图里先更新表单绑定的实例,再调用父类方法:

def forms_valid(self, form, inlines):
    password1 = form.cleaned_data['password1']
    password2 = form.cleaned_data['password2']
    if password1 and password2:
        if password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        # 注意这里修改的是form.instance,而非self.request.user
        form.instance.set_password(password1)
    # 现在父类保存的是已经更新密码的实例
    return super().forms_valid(form, inlines)

这种方法也能解决问题,但不如在表单里处理更符合Django的表单验证逻辑,推荐第一种方案。

内容的提问来源于stack exchange,提问作者ACA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:54:00