Метод clean() в Django ModelForm для предотвращения дублирования записей создает еще один экземпляр при обновлении данных. И даже не сохраняет новый экземпляр.

У меня есть несколько моделей, две из которых следующие:

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})

Форма модели выглядит следующим образом:

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 запроса fm_discharge=DischargeForm() в views.py

и он работал нормально для новых создаваемых экземпляров. Но для экземпляра, поступающего для обновления, он выбрасывал тот же ValidationError, потому что, очевидно, экземпляр discharge уже существует в базе данных для того же пациента. Затем, когда я добавил метод init и получил доступ к запросу и экземпляру, чтобы решить эту проблему, возникли две проблемы:

  1. It created a new instance of the data which was supposed to be just updated.
  2. As the new entry does not have an instance already, the init threw a KeyError for instance.

Что я могу здесь сделать? Как обрабатывать различные сценарии, подобные этому, в ModelForm?

Чтобы исправить это, не вставляйте instance в __init__, так как это приведет к тому, что вызов 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!')
Вернуться на верх