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

Django用户数据更新异常:更新时创建新用户,如何解决?

Django更新User模型时创建新用户而非覆盖数据的解决方法

问题描述

尝试更新Django中User模型的数据,但系统并未覆盖原有数据,反而尝试创建新用户,该如何解决?

相关代码

表单类代码

class SignUpForm(UserCreationForm):
    email = forms.EmailField(label="", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Email Address'}))
    first_name = forms.CharField(label="", max_length=100, widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'First Name'}))
    last_name = forms.CharField(label="", max_length=100, widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Last Name'}))

    class Meta:
        model = User
        fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2')

    def __init__(self, *args, **kwargs):
        super(SignUpForm, self).__init__(*args, **kwargs)

        self.fields['username'].widget.attrs['class'] = 'form-control'
        self.fields['username'].widget.attrs['placeholder'] = 'User Name'
        self.fields['username'].label = ''
        self.fields['username'].help_text = '<span class="form-text text-muted"><small>Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.</small></span>'

        self.fields['password1'].widget.attrs['class'] = 'form-control'
        self.fields['password1'].widget.attrs['placeholder'] = 'Password'
        self.fields['password1'].label = ''
        self.fields['password1'].help_text = '<ul class="form-text text-muted small"><li>Your password can\'t be too similar to your other personal information.</li><li>Your password must contain at least 8 characters.</li><li>Your password can\'t be a commonly used password.</li><li>Your password can\'t be entirely numeric.</li></ul>'

        self.fields['password2'].widget.attrs['class'] = 'form-control'
        self.fields['password2'].widget.attrs['placeholder'] = 'Confirm Password'
        self.fields['password2'].label = ''
        self.fields['password2'].help_text = '<span class="form-text text-muted"><small>Enter the same password as before, for verification.</small></span>'

views.py中的视图代码

def update_user(request):
    if request.user.is_authenticated:
        current_user = User.objects.get(id=request.user.id)
        profile_user = Profile.objects.get(user__id=request.user.id)
        # Get Forms
        user_form = SignUpForm(request.POST or None, request.FILES or None, instance=current_user)
        profile_form = ProfilePicForm(request.POST or None, request.FILES or None, instance=profile_user)
        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()

            login(request, current_user)
            messages.success(request, ("Your Profile Has Been Updated!"))
            return redirect('home')

        return render(request, "update_user.html", {'user_form':user_form, 'profile_form':profile_form})
    else:
        messages.success(request, ("You Must Be Logged In To View That Page..."))
        return redirect('home')

问题根源

你使用的UserCreationForm是Django专门用于创建新用户的表单,它的save()方法默认逻辑是生成新的User实例,即便传入了instance参数,也会因为密码字段的验证逻辑导致意外创建新用户,而非更新现有实例。

解决方法

方案1:创建专用的用户更新表单(不含密码字段)

如果不需要在更新页面修改密码,直接基于UserChangeForm创建更新表单,移除不必要的密码字段:

from django.contrib.auth.forms import UserChangeForm

class UserUpdateForm(UserChangeForm):
    email = forms.EmailField(label="", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Email Address'}))
    first_name = forms.CharField(label="", max_length=100, widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'First Name'}))
    last_name = forms.CharField(label="", max_length=100, widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Last Name'}))
    username = forms.CharField(label="", max_length=100, widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'User Name'}))

    class Meta:
        model = User
        fields = ('username', 'first_name', 'last_name', 'email')

    def __init__(self, *args, **kwargs):
        super(UserUpdateForm, self).__init__(*args, **kwargs)
        self.fields['username'].help_text = '<span class="form-text text-muted"><small>Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.</small></span>'
        # 删除默认的密码字段(UserChangeForm自带)
        del self.fields['password']

然后修改视图中的表单引用:

def update_user(request):
    if request.user.is_authenticated:
        current_user = User.objects.get(id=request.user.id)
        profile_user = Profile.objects.get(user__id=request.user.id)
        # 替换为新的更新表单
        user_form = UserUpdateForm(request.POST or None, instance=current_user)
        profile_form = ProfilePicForm(request.POST or None, request.FILES or None, instance=profile_user)
        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()

            messages.success(request, "你的资料已更新!")
            return redirect('home')

        return render(request, "update_user.html", {'user_form':user_form, 'profile_form':profile_form})
    else:
        messages.success(request, "你必须登录才能访问该页面...")
        return redirect('home')

方案2:保留密码更新功能,修改原表单的save逻辑

如果需要在更新页面允许修改密码,调整SignUpForm的save方法,确保传入instance时执行更新操作:

class SignUpForm(UserCreationForm):
    # 原有字段和__init__方法保持不变

    def save(self, commit=True):
        user = super().save(commit=False)
        # 仅当用户输入新密码时才更新,否则保留原密码
        if self.instance.pk:
            if self.cleaned_data.get('password1'):
                user.set_password(self.cleaned_data['password1'])
        else:
            user.set_password(self.cleaned_data['password1'])
        if commit:
            user.save()
        return user

同时修改视图,GET请求时清空密码字段,避免用户必须输入密码才能更新其他信息:

def update_user(request):
    if request.user.is_authenticated:
        current_user = User.objects.get(id=request.user.id)
        profile_user = Profile.objects.get(user__id=request.user.id)
        
        if request.method == 'GET':
            # GET请求时初始化密码字段为空
            user_form = SignUpForm(instance=current_user, initial={'password1': '', 'password2': ''})
        else:
            user_form = SignUpForm(request.POST, instance=current_user)
            
        profile_form = ProfilePicForm(request.POST or None, request.FILES or None, instance=profile_user)
        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()

            messages.success(request, "你的资料已更新!")
            return redirect('home')

        return render(request, "update_user.html", {'user_form':user_form, 'profile_form':profile_form})
    else:
        messages.success(request, "你必须登录才能访问该页面...")
        return redirect('home')

关键注意点

  • UserCreationForm的设计初衷是创建新用户,不要用它来做更新操作,这是核心问题。
  • 如果不需要更新密码,优先使用方案1,逻辑更简洁,避免密码验证带来的问题。
  • 若要保留密码更新,必须手动控制密码字段的处理逻辑,确保只有输入新密码时才更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 01:05:14