David Marogy
David Marogy

Reputation: 126

Django 4 update form fields dynamically using HTMX

I developed a Django application in which i have a form with some fields. Depending on the input additional fields are displayed are hidden. Now everything worked quit fine in Django 3.2.14 since the update in Django 4.0.6 it didn't worked anymore.

I first build a form, where if a "field_rule_display" exists the field widget is set as "HiddenInput".

class AnalysisForm(forms.Form):
    def __init__(self, analysis_form_template: AnalysisFormTemplate, disable_required: bool, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.layout = Layout()
        self.helper.add_input(Submit("submit", _("Evaluate"), css_class="btn-primary btn-lg"))
        analysis_field_queryset = analysis_form_template.analysis_fields
        analysis_form_url = reverse("analysis_form", args=(analysis_form_template.id,))
        for field in analysis_field_queryset.all():
            htmx_dictionary = _htmx_dictionary(analysis_form_url, field)
            self.fields[field.name_for_formula] = _get_field_by_type(
                field, htmx_dictionary, analysis_form_template, self.data
            )
            self.fields[field.name_for_formula].empty_values = empty_values()
            self.helper.layout.fields.append(
                Div(Field(field.name_for_formula), css_class=AnalysisFieldKind(field.kind).name)
            )
            if field.field_rule_display is not None and disable_required is False:
                self.fields[field.name_for_formula].widget = forms.HiddenInput()
                self.fields[field.name_for_formula].widget.attrs["disabled"] = True
            if disable_required:
                self.fields[field.name_for_formula].required = False

After the user enters a specific input into the form, htmx will send a request and i rebuild the form with the new fields. And here the problem starts, even if i update my field in the "self.fields" Django does not render the update and my form field is still hidden.

            if field.field_rule_display is not None:
                evaluated_result_display = self._evaluated_formula(
                    field,
                    analysis_form_template,
                    field.field_rule_display,
                    field.field_rule_display.formula,
                    cleaned_data,
                )
                if evaluated_result_display:
                    field_type = _get_field_by_type(
                        field, htmx_dictionary, analysis_form_template, cleaned_data
                    )
                    self.fields[field.name_for_formula] = field_type
                    self.fields[field.name_for_formula].initial = cleaned_data[field.name_for_formula]

enter image description here

Here the second field should be displayed, but only my border is shown as a result of changing the crispy layout helper. enter image description here

            if (
                field.field_rule_display is None or (field.field_rule_display is not None and evaluated_result_display)
            ) and field.field_rule_highlight is not None:
                evaluated_result_highlight = self._evaluated_formula(
                    field.name_for_formula,
                    analysis_form_template,
                    field.field_rule_highlight,
                    field.field_rule_highlight.formula,
                    cleaned_data,
                )
                if evaluated_result_highlight:
                    field_layout = Div(
                        Field(field.name_for_formula),
                        css_class=f"{AnalysisFieldKind(field.kind).name} border border-primary mb-2 p-2",
                    )
            self.fields[field.name_for_formula].empty_values = empty_values()
            field_layout_list.append(field_layout)
        self.helper.layout.fields = field_layout_list

I would appreciate some help, why this does not work anymore in Django 4 but in Django 3 it worked without a problem.

Upvotes: 2

Views: 627

Answers (1)

David Marogy
David Marogy

Reputation: 126

After some debugging i found a possible solution to my problem.

It seems that Django caches my form fields and their widgets in "_bound_fields_cache". Now there my fields widget is still set to "HiddenInput", even after updating the widget to a "TextInput".

enter image description here enter image description here

So i tried updating the field in "_bound_fields_cache" and to my suprise it worked.

            if field.field_rule_display is not None:
                evaluated_result_display = self._evaluated_formula(
                    field,
                    analysis_form_template,
                    field.field_rule_display,
                    field.field_rule_display.formula,
                    cleaned_data,
                )
                if evaluated_result_display:
                    field_type = _get_field_by_type(
                        field, htmx_dictionary, analysis_form_template, cleaned_data
                    )
                    self.fields[field.name_for_formula] = field_type
                    self.fields[field.name_for_formula].initial = cleaned_data[field.name_for_formula]
                    if field.name_for_formula in self._bound_fields_cache:
                        self._bound_fields_cache[field.name_for_formula].field = field_type

enter image description here enter image description here

But i am not quite satisfied with this kind of solution. I can't really point my finger why Django doesn't change the "_bound_fields_cache" after updating my form fields. Changing the "_bound_fields_cache" seems for me like an ugly hack... Is there perhaps a better solution?

Upvotes: 1

Related Questions