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
相关产品推荐
相关产品推荐

