2

I try to use formset and CBV CreateView for a treatment model but validation failed. I am quite lost with validation logic in my view. I have added a try/catch in form_valid method to manage unicity constraint but this might not be the right way to do.

Moreover, other form validations are not displayed in my template.


@method_decorator(login_required, name="dispatch")
class TraitementFormSetCreate(SuccessMessageMixin, CreateView):

    model = Traitement
    form_class = TraitementForm
    template_name = "ecrf/traitements_form.html" 
    success_message = "Fiche Traitement créée."
    success_url = reverse_lazy('ecrf:patient_list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['formset'] = TraitementFormSet(queryset=Traitement.objects.filter(pat=self.kwargs['pk']))
        context['action'] = 'create'
        context['patient'] = Patient.objects.get(ide=self.kwargs['pk'])
        context['model_name'] = self.model._meta.model_name
        context['is_locked'] = self.object.ver if self.object else None
        return context

    def post(self, request, *args, **kwargs):
        self.object = self.get_object() # assign the object to the view
        formset = TraitementFormSet(request.POST)
        if formset.is_valid():
            return self.form_valid(formset)
        else:
            return self.form_invalid(formset)

    def form_valid(self, formset):
        print('valid')
        patient = Patient.objects.get(ide=self.kwargs["pk"])
        traitements = formset.save(commit=False)
        for traitement in traitements:
            traitement.pat = patient
            traitement.arv_sai_log = self.request.user.username
            try:
                traitement.save()
            except IntegrityError:
                formset.errors.append(ValidationError('Une fiche Traitement ARV existe déjà pour ce patient avec ce numéro'))
                context = self.get_context_data(formset=formset)
                return self.render_to_response(context)
        return redirect("ecrf:patient_list")

    def form_invalid(self, formset):
        print('invalid',formset.errors)
        context = self.get_context_data(formset=formset)
        return self.render_to_response(context)

Update

I try using FBV (logic more easy to understand for me).

Maybe I should have used inlineformset_facory to deal with FK but for conception concerns patient is passed in context.

I faced many issues I've tried to solved but it is more "hack" and I would like to find a proper way to do the same:

  1. empty_forms was submitted and create a record in database; normally, default formset behavior is not to submit empty formset I manage this case using condition form.cleaned_data != {}. It is maybe cause

  2. I define unique_together in my model Traitement. But it seems this is not manage by Django in form validation. When submitting a form with duplicates, IntegrityError is raised. I manage this case using a try-catch bloc. I also tried to define a form validation with clean() method in TraitementForm but doesn't seems to works.

  3. With traitement_create() code below, all errors can not be displayed at the same time: forma validation will be displayed but not the IntegretyError if any.

I have read many documentation and tutorial but could not find answer.

#models.py

class Patient(models.Model):
    ide = models.AutoField(primary_key=True)
    pat = models.CharField("Patient",max_length=5, unique=True, null=True, blank=True, error_messages={'unique':'Un patient avec ce code existe déjà'})

class Traitement(models.Model):
    ide = models.AutoField(primary_key=True)
    pat = models.ForeignKey('Patient',on_delete = models.CASCADE, related_name='traitement_patient', db_column='pat')
    arv_num = models.IntegerField('Réf/code', null=True, blank=True) 


# views.py
def traitements_create(request, pk):
    template_name = 'ecrf/traitements_form.html'
    patient = Patient.objects.get(ide=pk)

    if request.method == 'POST':
        formset = TraitementFormSet(request.POST or None, queryset=Traitement.objects.filter(pat=pk)) 
        if formset.is_valid():
            error_occurred = False
            for form in formset:
                if form.is_valid() and form.cleaned_data != {}:
                    try:
                        traitement = form.save(commit=False)
                        traitement.pat = patient
                        traitement.arv_sai_log = request.user.username
                        traitement.save()
                    except IntegrityError:
                        form.add_error('arv_num', 'Une fiche Traitement ARV existe déjà pour ce patient avec ce numéro')
                        error_occurred = True
                        break  
            if not error_occurred:
                return redirect('ecrf:patient_list')

    else:
        formset = TraitementFormSet(queryset=Traitement.objects.filter(pat=pk))
    return render(request, template_name, {'formset': formset, 'patient': patient})

# forms.py

class TraitementForm(forms.ModelForm):
    
    ide = forms.IntegerField()
    arv_num = forms.IntegerField(label = 'Réf/code',widget=forms.TextInput(attrs={'class': 'form-control','placeholder': '','data-mask':'00'})) 
    arv_deb_dat = forms.DateField(label = 'Date de début', widget=forms.DateInput(attrs={'class': 'form-control datepicker2', 'autocomplete': 'off', 'placeholder': '', 'data-date-format': 'dd/mm/yyyy', 'data-theme':'dark'}, format='%d/%m/%Y') ,input_formats=settings.DATE_INPUT_FORMATS,required=False)

    class Meta:
        model = Traitement
        fields = ['ide','arv_num','arv_deb_dat',...,]
          

    def clean_arv_deb_dat(self):
        data = self.cleaned_data['arv_deb_dat']
        if data:
            entrydate = datetime.datetime.strptime(str(data), "%Y-%m-%d")
            currentdate = datetime.datetime.now()
            if entrydate > currentdate:
                raise forms.ValidationError("Merci de vérifier la date")
        return data

TraitementFormSet = modelformset_factory(
    Traitement, 
    form=TraitementForm,
    extra=1,
)
halfer
  • 19,824
  • 17
  • 99
  • 186
SLATER
  • 613
  • 9
  • 31

0 Answers0