在Django的UpdateView中使用update_session_auth_hash触发唯一约束错误
解决Django中UpdateView更新用户时触发Profile唯一约束错误
问题根源
你的ProfileUpdateForm的save方法每次执行都会调用Profile.objects.create()创建新的Profile记录,但当前用户已经存在对应的一对一关联Profile,重复创建会触发phoneNumber的唯一约束,导致IntegrityError。同时视图的form_valid方法重复调用form.save(),进一步加剧了这个问题。
解决方案
1. 修改表单的save方法:更新而非重复创建Profile
将创建逻辑改为获取已有Profile并更新,不存在时再创建,同时补充表单初始化填充和手机号唯一性验证:
class ProfileUpdateForm(UserCreationForm): phoneNumber = forms.CharField(required=False) address = forms.CharField(required=False) username = forms.CharField() email = forms.EmailField() password1 = forms.CharField(widget=forms.PasswordInput) password2 = forms.CharField(widget=forms.PasswordInput) class Meta(UserCreationForm.Meta): fields = UserCreationForm.Meta.fields + ('email',) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['username'].disabled = True # 初始化表单时填充已有Profile数据 if self.instance.pk: try: profile = self.instance.profile self.fields['phoneNumber'].initial = profile.phoneNumber self.fields['address'].initial = profile.address self.fields['email'].initial = self.instance.email except Profile.DoesNotExist: pass def clean_phoneNumber(self): # 验证手机号唯一性,排除当前用户的手机号 phone_number = self.cleaned_data.get('phoneNumber') if phone_number: duplicate = Profile.objects.exclude(user=self.instance).filter(phoneNumber=phone_number).exists() if duplicate: raise forms.ValidationError("该手机号已被其他用户使用") return phone_number def save(self, commit=True): user = super().save(commit=commit) # 获取或创建用户对应的Profile profile, created = Profile.objects.get_or_create(user=user) # 更新Profile字段 profile.phoneNumber = self.cleaned_data['phoneNumber'] profile.address = self.cleaned_data['address'] profile.username = user.username profile.email = user.email if commit: profile.save() return user
2. 修正视图的form_valid方法,避免重复保存
原视图中两次执行保存逻辑会导致重复创建/更新,修改为:
class UserUpdate(LoginRequiredMixin, UpdateView): model = User form_class = ProfileUpdateForm template_name = 'single_pages/profile_update.html' success_url = reverse_lazy('single_pages:landing') login_url = '/login/' def form_valid(self, form): # 调用父类方法完成保存,表单已处理好Profile response = super().form_valid(form) # 更新会话认证哈希,保持用户登录状态 update_session_auth_hash(self.request, self.object) return response
3. 修正Profile模型的正则表达式(可选)
原正则存在多余竖线,修正为合法格式:
phoneNumberRegex = RegexValidator(regex=r'^01([016789])-?([0-9]{3,4})-?([0-9]{4})$')
额外说明
- Profile模型中的
username和email字段与User模型冗余,建议直接从User模型获取,避免数据不一致。 - 即便将Profile改为ForeignKey,核心逻辑仍需保持:更新已有关联记录而非重复创建,同时确保
phoneNumber的唯一性。
内容的提问来源于stack exchange,提问作者gotnotime
相关产品推荐
相关产品推荐

