Django表单校验失败时重置只读字段为初始值并保留错误提示
问题根因
当前逻辑存在两处核心错误,才会导致你遇到的矛盾现象:
- 表单校验触发只读字段的错误后,渲染时默认读取用户提交的原始值填充输入框,因此会显示篡改后的非法内容
- 校验失败分支中你重新实例化了一个绑定当前用户实例的全新表单,原表单上挂载的所有校验错误信息会被直接丢弃,自然无法在模板中展示
修复方案
不需要修改前端模板代码,仅需调整表单类和视图类的两处逻辑即可:
1. 修改forms.py字段清洗逻辑
在clean_email和clean_user_name两个只读字段的清洗方法中,检测到字段值被篡改时,先将字段清洗值重置为数据库中存储的用户原始值,再抛出校验错误。修改后的两个清洗方法代码如下:
def clean_user_name(self): username = self.cleaned_data['user_name'] if username != self.instance.user_name: # 重置为数据库原始值,避免渲染时显示篡改内容 self.cleaned_data['user_name'] = self.instance.user_name raise forms.ValidationError('抱歉,您不能修改用户名') return self.instance.user_name def clean_email(self): email = self.cleaned_data['email'] if email != self.instance.email: # 重置为数据库原始值 self.cleaned_data['email'] = self.instance.email raise forms.ValidationError('抱歉,您不能修改注册邮箱') return self.instance.email
逻辑说明:Django表单渲染输入框的初始值时,会优先读取
cleaned_data中存储的清洗后的值,提前将被篡改字段的清洗值替换为数据库原始值,就能保证输入框始终展示正确内容,不会回显非法篡改值。
2. 修改views.py的POST处理逻辑
删除校验失败分支中重新实例化表单的代码,直接将携带校验错误的原表单传给form_invalid方法即可。修改后的post方法代码如下:
def post(self, request, *args, **kwargs): form = self.get_form() if form.is_valid(): return self.form_valid(form) # 校验失败直接返回原表单,保留错误信息和用户输入的合法字段值 return self.form_invalid(form)
最终效果
修复完成后可以实现你需要的全部需求:
- 用户篡改只读字段提交时,页面正常展示对应的字段不可修改的错误提示
- email、user_name两个只读字段自动回填数据库中的正确值,不会显示用户篡改的非法内容
- 可编辑字段first_name会保留用户本次提交的输入内容,无需用户重复填写
内容的提问来源于stack exchange,提问作者Farshad Zargari
相关产品推荐
相关产品推荐

