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 方法并访问请求和实例来解决这个问题时,出现了两个问题:
- 它创建了一个应该刚刚更新的数据的新实例。
- 由于新条目还没有一个实例,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!')
我有几个模型,其中两个如下:
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 方法并访问请求和实例来解决这个问题时,出现了两个问题:
- 它创建了一个应该刚刚更新的数据的新实例。
- 由于新条目还没有一个实例,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!')