如何处理Django ModelForm提交时出现的竞态条件问题
触发原因梳理
该问题的核心是第一个标签页提交时,Form绑定的实例的text_field_2字段已经被修改为None,常见触发路径如下:
1. 并发修改导致实例数据变更
Django的UpdateView处理POST请求时,会先调用get_object()从数据库查询最新的目标实例,再绑定到Form上:
- 两个标签页同时打开表单时,加载的都是初始值
text_field_2='world' - 第二个标签页先提交,将数据库中对应实例的
text_field_2字段更新为None - 第一个标签页提交时,
UpdateView拉取到的已经是被修改后的实例,因此clean方法中打印结果为None
第二个标签页会把字段更新为None,通常是两个配置叠加导致: MyModel的text_field_2字段设置了null=True, blank=True,空字符串提交时会被Django自动转换为None存入数据库- 提交第二个标签页表单时,
text_field_2输入框为空,且Form中该字段设置了required=False,校验通过后空值被写入数据库
2. 同一请求内form.save()重复调用导致数据覆盖
如果视图的form_valid方法中重复调用了form.save(),也会触发该问题:
- 第一次调用
form.save()时,正常按照提交数据更新实例 - 若两次调用之间修改了
form.instance的text_field_2属性为None,或者cleaned_data中text_field_2的值为空,第二次调用save()时会再次把空值写入实例并同步到数据库
注意:form.save()是当前请求Form实例的内置方法,不需要传入实例参数。所谓“第二次调用没传实例”的观察,大概率是二次实例化Form时没有传入instance参数,此时新Form对应新建对象逻辑,不会影响原有实例。
3. Form字段配置错误导致值丢失
如果ModelForm的Meta类指定fields时,未将text_field_2加入可写字段列表,该字段不会被ModelForm自动处理:
- 若业务代码中手动修改
instance.text_field_2为None后再保存,空值会被写入数据库 - 后续其他请求拉取到的实例对应字段自然返回
None
内容的提问来源于stack exchange,提问作者alias51
相关产品推荐
相关产品推荐

