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

Django 4.1:从ModelForm创建User对象前排除字段报错

解决ModelForm新增confirm字段导致创建对象时的关键字参数错误

我有一个继承自自定义User模型的Patient用户类型,在ModelForm中新增了非模型字段confirm(用于确认密码),尝试在创建用户对象前移除该字段,但运行时出现错误:got unexpected keyword arguments: 'confirm'。相关代码如下:

models.py

class User(AbstractUser):
    username = None

    email = models.EmailField(unique=True)

    is_patient = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'

    REQUIRED_FIELDS = ['password']

    object = UserManager()

class Patient(User):
    address = models.CharField(max_length=200)

    class Meta:
        verbose_name_plural = 'Patients'

    def __str__(self):
        return self.first_name

forms.py

class PateintForm(forms.ModelForm):
    
    confirm = forms.CharField(max_length=50, widget=forms.PasswordInput)
    is_patient = forms.BooleanField(widget=forms.HiddenInput)

    class Meta:
        model = Patient
        fields = ['first_name', 'last_name', 'address', 'email', 'password']

        widgets = {
            'first_name': forms.TextInput(attrs={'placeholder': 'First Name'}),
            'last_name': forms.TextInput(attrs={'placeholder': 'Last Name'}),
            'address': forms.TextInput(attrs={'placeholder': 'Address'}),
            'email': forms.EmailInput(attrs={'placeholder': 'Email'}),
            'password': forms.PasswordInput(attrs={'placeholder': 'Password'})
        }

    def clean(self):
        cleaned_data = self.cleaned_data
        password = cleaned_data.get("password")
        confirm = cleaned_data.get("confirm")
        if confirm != password:
            self.add_error("password", "Passwords do not match.")
            self.add_error("confirm", "Passwords do not match.")
        if len(password) < 8:
            self.add_error("password", "Password must be greater than 8 letters.")
        if confirm == password and len(password) > 8:
            del cleaned_data['confirm']
            return cleaned_data

views.py

def Patient(request):
    if request.method == "POST":
        form = PatientForm(request.POST, initial={'is_patient': True})
        if form.is_valid():
            Patient.objects.create(**form.cleaned_data)
            return HttpResponseRedirect('/Login/')

        else:
            for field in form.errors:
                form[field].field.widget.attrs['class'] += ' is-invalid'
            return render(request, 'Home/RegPatient.html', {'form': form})
    
    else:
        form = PatientForm(initial={'is_patient': True})
        return render(request, 'Home/RegPatient.html', {'form': form})

问题原因

原clean方法仅在密码匹配且长度达标时才删除confirm字段,一旦验证不通过(比如密码不匹配或长度不够),cleaned_data中会保留confirm键,传给Patient.objects.create()时,因模型无该字段触发关键字参数错误。

解决方案

1. 修正clean方法的字段处理逻辑

确保无论验证结果如何,都移除confirm字段,避免多余参数传入模型创建方法:

def clean(self):
    cleaned_data = super().clean()
    password = cleaned_data.get("password")
    confirm = cleaned_data.get("confirm")

    # 密码匹配验证
    if confirm != password:
        self.add_error("password", "密码不匹配")
        self.add_error("confirm", "密码不匹配")
    # 密码长度验证
    if password and len(password) < 8:
        self.add_error("password", "密码长度必须大于8位")
    
    # 安全移除confirm字段,即使字段不存在也不会报错
    cleaned_data.pop('confirm', None)
    return cleaned_data

2. 正确创建用户(修复密码存储问题)

由于User模型继承自AbstractUser,直接用create()会明文存储密码,必须使用create_user()方法自动哈希密码:

def Patient(request):
    if request.method == "POST":
        form = PatientForm(request.POST, initial={'is_patient': True})
        if form.is_valid():
            user_data = form.cleaned_data
            # 单独取出密码,用create_user处理哈希
            password = user_data.pop('password')
            patient = Patient.objects.create_user(password=password, **user_data)
            return HttpResponseRedirect('/Login/')

        else:
            for field in form.errors:
                form[field].field.widget.attrs['class'] += ' is-invalid'
            return render(request, 'Home/RegPatient.html', {'form': form})
    
    else:
        form = PatientForm(initial={'is_patient': True})
        return render(request, 'Home/RegPatient.html', {'form': form})

3. 修正表单类拼写错误

表单类名PateintForm应为PatientForm,避免调用时出现名称错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 23:40:31