Thank you so much for trying to help !
In the end, the following code is working properly.
The code needs to be cleaned a little bit, but I can display only the fields I need and save them.
class PermittedDataType(Enum):
STRING = "STR"
NUMBER = "NB"
BOOLEAN = "BOOL"
class ObservationDefinition(CanBeDisabledModel):
DATA_TYPE_CHOICES = [
(PermittedDataType.STRING.value, "Texte"),
(PermittedDataType.NUMBER.value, "Nombre"),
]
name = models.CharField(max_length=250, verbose_name=_("nom"))
permitted_data_type = models.CharField(
max_length=4,
choices=DATA_TYPE_CHOICES,
null=True,
blank=True,
)
class ObservationProfile(models.Model):
title = models.CharField(max_length=250)
class ObservationProfileElement(models.Model):
profile = models.ForeignKey(ObservationProfile, on_delete=models.PROTECT, related_name="element")
definition = models.ForeignKey(ObservationDefinition, on_delete=models.PROTECT)
sort_order = models.IntegerField(default=1)
class Observation(models.Model):
performer = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
blank=False,
null=False,
)
defined_by = models.ForeignKey(
ObservationDefinition,
on_delete=models.PROTECT,
blank=False,
null=False,
verbose_name=_("défini par"),
)
from_profile = models.ForeignKey(ObservationProfile, on_delete=models.PROTECT, null=True)
value_string = models.TextField(blank=True, null=True)
value_boolean = models.BooleanField(blank=True, null=True)
value_number = models.FloatField(blank=True, null=True)
class ObservationCreateOnPost(FormView):
model = Observation
form_class = ObservationForm
template_name = "observations/observation_form.html"
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
profile_id = 1
elements = ObservationProfile.objects.get(pk=profile_id).element.all()
if not context.get("formset"):
formset_class = modelformset_factory(
self.model, form=self.form_class, extra=len(elements)
)
formset = formset_class(queryset=Observation.objects.none())
for element, form in zip(elements, formset):
initial = {
"defined_by": element.definition.id,
"from_profile": profile_id,
"subject": 1,
"encounter": 1,
}
form.initial = initial
self.cleanup_fields(form, element.definition.permitted_data_type)
context["formset"] = formset
return context
def cleanup_fields(self, form, data_type_to_show: str):
"""
Remove the not needed fields depending on the type of data. The user only needs to fill in the type of data from ObservationDefinition.permitted_data_type.
"""
for t in PermittedDataType:
if t.value != data_type_to_show:
del form.fields[self.type_to_field(t.value)]
def type_to_field(self, data_type: PermittedDataType):
"""
Map a data type from the ObservationDefinition to it's corresponding field in the Observation object
"""
if data_type == PermittedDataType.NUMBER.value:
return "value_number"
elif data_type == PermittedDataType.STRING.value:
return "value_string"
elif data_type == PermittedDataType.BOOLEAN.value:
return "value_boolean"
def post(self, request, *args, **kwargs):
print("--- in POST")
print(request.POST)
formset = modelformset_factory(self.model, form=self.form_class)(request.POST)
for form in formset:
# set the author
setattr(form.instance, "performer", self.request.user)
if formset.is_valid():
return self.form_valid(formset)
else:
return self.form_invalid(formset)
def form_invalid(self, formset):
return self.render_to_response(self.get_context_data(formset=formset))
def form_valid(self, formset):
formset.save()
return super().form_valid(formset)
def get_success_url(self):
return reverse_lazy("observation")