You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

解决密码修改页面触发duplicate key unique约束冲突问题

问题分析与解决方案

这个错误的核心原因很明确:你的表单在提交时试图创建一个新的User实例,而不是更新现有患者关联的User对象,并且因为表单里包含了username字段但提交时没有传入有效值(变成空字符串),触发了用户名唯一约束的冲突。

下面分步给出修复方案:

1. 简化密码修改表单,移除不必要字段

这是专门的密码修改页面,不需要包含usernamefirst_namelast_name这些字段,既避免空值问题,也符合页面功能定位。同时要重写save方法,正确处理密码加密存储:

修改forms.py

class CallcenterPasswordChange(forms.ModelForm):
    password1 = forms.CharField(widget=forms.PasswordInput(), label='Новый пароль')
    password2 = forms.CharField(widget=forms.PasswordInput(), label='Повтор нового пароля')

    def clean(self):
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError(
                self.error_messages['password_mismatch'],
                code='Повтор нового пароля не совпадает',
            )
        return self.cleaned_data

    # 重写save方法,正确设置加密后的密码
    def save(self, commit=True):
        user = super().save(commit=False)
        # 使用Django内置方法加密密码
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user

    class Meta:
        model = User
        # 只保留密码相关字段
        fields = ('password1', 'password2')

2. 修正视图的表单实例化逻辑

在POST请求中,必须把现有的patient.user作为instance参数传给表单,这样表单才会执行更新操作,而不是创建新用户:

修改views.pypost方法:

def post(self, request, **kwargs):
    context = super().get_context_data(**kwargs)
    patient_pk = kwargs.get('patient_pk')
    patient = get_object_or_404(Patient, pk=patient_pk)
    
    # 关键:传入instance参数,指定要更新的用户对象
    form = CallcenterPasswordChange(request.POST, instance=patient.user)
    context['form_user'] = form
    context['patient'] = patient
    
    if form.is_valid():
        form.save()
        # 这里可以添加密码修改成功后的跳转逻辑,比如:
        # return redirect('patient_detail', pk=patient_pk)
    # 表单无效时返回原页面展示错误
    return render(request, template_name=self.template_name, context=context)

同时简化get_context_data里的表单初始化:

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    patient_pk = kwargs.get('patient_pk')
    patient = get_object_or_404(Patient, pk=patient_pk)
    # 直接传入instance,无需手动处理模型字段
    context['form'] = CallcenterPasswordChange(instance=patient.user)
    context['patient_pk'] = patient_pk
    context['patient'] = patient
    return context

补充说明:之前代码出错的关键点

  • 未传入instance参数:默认情况下ModelForm会创建新实例,而非更新现有对象;
  • 表单包含冗余字段:username字段未被正确填充时会提交空值,触发唯一约束冲突;
  • 未处理密码加密:直接保存明文密码不仅会导致登录失败,原表单的save方法也不会自动识别password1/password2字段。

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

火山引擎 最新活动