Django ModelForm clean()更新时新建实例、重复校验问题求解
Django ModelForm 出院记录唯一校验及更新问题解决方案
问题根因
- 你在自定义
__init__方法中提前将instance参数从kwargs中弹出,导致父类ModelForm无法识别当前是更新场景,默认走新建逻辑,最终更新操作变成插入新记录 - 新建场景下没有传入
instance参数,执行kwargs.pop('instance')时没有匹配的key,直接抛出KeyError - 最初的校验逻辑没有排除当前正在更新的实例,导致更新时查询到自身记录触发重复报错
- 你对
self.instance的判断逻辑错误:ModelForm无论新建还是更新场景,self.instance永远存在(新建时为未保存的空白模型实例,pk为None),因此if not self.instance的判断永远不成立,校验逻辑完全失效
修复方案
直接替换你的DischargeForm为以下实现即可,原有视图代码无需修改:
class DischargeForm(ModelForm): class Meta: model = Discharge fields = '__all__' widgets = { 'date_of_discharge': DateInput(attrs={'type': 'date'}), } def __init__(self, *args, **kwargs): # 仅弹出自定义传入的request参数,instance留给父类自动处理 self.request = kwargs.pop('request') super().__init__(*args, **kwargs) def clean(self): cleaned_data = super().clean() pt = cleaned_data.get('patient') # 患者字段为空的话直接返回,交给默认的必填校验处理 if not pt: return cleaned_data rec = Receivables.objects.filter(patient=pt).order_by('-id').first() if not rec: raise ValidationError('未查询到该患者的对应应收款记录') # 构造重复查询条件 discharge_query = Discharge.objects.filter( patient=pt, date_of_discharge__gt=rec.approval_date ) # 如果是更新场景,排除当前正在编辑的记录,避免查询到自身触发报错 if self.instance.pk is not None: discharge_query = discharge_query.exclude(pk=self.instance.pk) if discharge_query.exists(): raise ValidationError('该患者已存在有效出院记录,请勿重复创建') return cleaned_data
可选优化(避免并发场景重复创建)
建议在模型层增加唯一约束,从数据库层面保障数据唯一性,避免高并发场景下Form校验的时间差导致重复插入:
class Discharge(models.Model): patient = models.ForeignKey(Patient, on_delete=CASCADE) # 其余原有字段保持不变 date_of_discharge = models.DateField(default=None) mould_charges = models.DecimalField(max_digits=7, decimal_places=2, default=0, blank=True) ct_charges = models.DecimalField(max_digits=7, decimal_places=2, default=0, blank=True) discharge_updated = models.BooleanField(default=False) class Meta: # 如果单个患者仅允许一条出院记录,用这个约束 constraints = [ models.UniqueConstraint(fields=['patient'], name='unique_patient_discharge') ] # 如果是每次就诊对应一条出院记录,建议新增就诊外键后用联合约束: # constraints = [ # models.UniqueConstraint(fields=['patient', 'visit_record'], name='unique_patient_visit_discharge') # ]
内容的提问来源于stack exchange,提问作者noob87
相关产品推荐
相关产品推荐

