Django ModelForm 中的 clean() 方法避免重复条目在更新数据时创建另一个实例。甚至不保存新实例

clean() method in Django ModelForm to avoid duplicate entries creates another instance upon updating the data. And doesn't even save a new instance

我有几个模型,其中两个如下:

class Receivables(models.Model):
    patient=models.ForeignKey(Patient, on_delete=CASCADE)
    pattern = RegexValidator(r'(RT|rt|rT|Rt)\/[0-9]{4}\/[0-9]{2}\/[0-9]{4}', 'Enter RT Number properly!')
    rt_number=models.CharField(max_length=15, validators=[pattern])
    discount=models.DecimalField(max_digits=9, decimal_places=2, default=0)
    approved_package=models.DecimalField(max_digits=10, decimal_places=2, default=0)
    approval_date=models.DateField(default=None)
    proposed_fractions=models.IntegerField()
    done_fractions=models.IntegerField()
    base_value=models.DecimalField(max_digits=10, decimal_places=2, blank=True)
    expected_value=models.DecimalField(max_digits=10, decimal_places=2, blank=True)

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)

保存新实例和更新现有实例的视图分别是:

1.

def discharge_view(request):
    if request.method=='POST':
        fm_discharge=DischargeForm(request.POST, request=request)
        if fm_discharge.is_valid():
            discharge=fm_discharge.save()
            ipd=IpdReport.objects.create(patient=discharge.patient, package=Package.objects.filter(patient=discharge.patient).order_by('-id').first(), receivables=Receivables.objects.filter(patient=discharge.patient).order_by('-id').first(), discharge=discharge)
            if discharge is not None:
                OngoingReport.objects.filter(ipdreport__patient=discharge.patient).delete()
                package=Package.objects.filter(patient=discharge.patient).order_by('-id').first().patient_type.patient_type
                if discharge.discharge_updated==False and package!='CASH':
                    UnclaimedPendingCases.objects.create(ipdreport=ipd)
                elif discharge.discharge_updated==True and package!='CASH':
                    ClaimedPendingCases.objects.create(ipdreport=ipd)
            fm_discharge=DischargeForm(request=request)
        return render(request, 'account/discharge.html', {'form':fm_discharge})
    else:
        fm_discharge=DischargeForm(request=request)
        return render(request, 'account/discharge.html', {'form':fm_discharge})
def update_discharge_view(request, id):
    di1=Discharge.objects.get(pk=id)
    fm1=di1.discharge_updated
    if request.method=='POST':
        print(request.POST)
        di=Discharge.objects.get(pk=id)
        form=DischargeForm(request.POST, instance=di, request=request)
        if form.is_valid():
            discharge=form.save()
    else:
        di=Discharge.objects.get(pk=id)
        form=DischargeForm(instance=di, request=request)
    return render(request, 'account/update_discharge.html', {'form':form})

ModelForm 如下所示:

class DischargeForm(ModelForm):
    class Meta:
        model=Discharge
        fields='__all__'
        widgets={
            'date_of_discharge': DateInput(attrs={'type': 'date'}),
        }

    def __init__(self, *args, **kwargs):
        self.request=kwargs.pop('request')
        self.instance=kwargs.pop('instance')
        super(DischargeForm, self).__init__(*args, **kwargs)

    def clean(self):
        super().clean()
        pt=self.request.POST.get('patient')
        if not self.instance:
            rec=Receivables.objects.filter(patient__pk=pt).order_by('-id').first()
            if Discharge.objects.filter(patient__pk=pt, date_of_discharge__gt=rec.approval_date).exists():
                raise ValidationError('The patient has already been discharged!')

我希望出院只保存一次,每次患者接受治疗。虽然可以更新。之前我是这样写的:

class DischargeForm(ModelForm):
    class Meta:
        model=Discharge
        fields='__all__'
        widgets={
            'date_of_discharge': DateInput(attrs={'type': 'date'}),
        }

    def clean(self):
        super().clean()
        pt=self.cleaned_data['patient']
        rec=Receivables.objects.filter(patient__pk=pt).order_by('-id').first()
        if Discharge.objects.filter(patient__pk=pt, date_of_discharge__gt=rec.approval_date).exists():
            raise ValidationError('The patient has already been discharged!')

没有将请求 kwargs 传递给 views.py

中的 fm_discharge=DischargeForm()

它对正在创建的新实例运行良好。但是它为进入更新的实例抛出了相同的 ValidationError,因为显然 discharge 实例已经存在于同一患者的数据库中。然后,当我添加 init 方法并访问请求和实例来解决这个问题时,出现了两个问题:

  1. 它创建了一个应该刚刚更新的数据的新实例。
  2. 由于新条目还没有一个实例,init 抛出了一个 KeyError 例如。

我可以在这里做什么?如何在ModelForm中处理诸如此类的不同场景?

要解决此问题,请不要在 __init__ 中弹出 instance,因为这样做会导致 super() 调用告诉 ModelForm 它将会工作关于创建 一个新对象。本质上就像实例根本没有传递给表单一样。

对于 clean 调用,只需添加一个标志,即在 创建 一个实例(而不是更新时)时,验证应该只 运行 使用 self.instance.pk,所以:

    def clean(self):
        super().clean()

        if not self.instance.pk:
            pt=self.cleaned_data['patient']
            rec=Receivables.objects.filter(patient__pk=pt).order_by('-id').first()
            if Discharge.objects.filter(patient__pk=pt, date_of_discharge__gt=rec.approval_date).exists():
                raise ValidationError('The patient has already been discharged!')