You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 01:24:07