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:
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
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.
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,
)