Search code examples
pythondjangopython-requestsdjango-formshtmx

Django form error "this field is required" after HTMX request


I'm using HTMX lib to send the requests with Django. The issue is that after succeed htmx request with project_form swapped project_detail_form comes with "this field is required". What am I doing wrong?

This is my views.py:

def index(request):
    """Index view."""
    project_form = ProjectForm(data=request.POST or None)
    project_detail_form = ProjectDetailForm(data=request.POST or None)
    if request.method == "POST":
        try:
            if project_form.is_valid():
                project_form.save()
                return render(
                    request,
                    "monitor/_project_detail_form.html",
                    context={"project_detail_form": project_detail_form},
                )
        except Exception as e:
            return HttpResponse(f"Error: {e}")
    return render(request, "monitor/index.html", context={"project_form": project_form})

A part of index.html

            <form class="pt-4" hx-post="/" hx-target="this" hx-swap="innerHTML" enctype="multipart/form-data">
                {% csrf_token %}
                {{ project_form.as_div }}
                <button class="btn btn-primary" type="submit">Save</button>
            </form>

_project_detail_form.html

{% csrf_token %}
{{ project_detail_form.as_div }}
<button class="btn btn-primary" type="submit">Save</button>

forms.py

class ProjectForm(forms.ModelForm):
    """Project form."""

    name = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"}))
    check_interval = forms.IntegerField(
        widget=forms.NumberInput(attrs={"class": "form-control"})
    )
    is_active = forms.BooleanField(
        required=False, widget=forms.CheckboxInput(attrs={"class": "form-check"})
    )

    class Meta:
        """Meta."""

        model = Project
        fields = "__all__"


class ProjectDetailForm(forms.ModelForm):
    """Project Detail form."""

    project = forms.ModelChoiceField(
        queryset=Project.objects.all(),
        widget=forms.Select(attrs={"class": "form-select"}),
    )
    url = forms.URLField(widget=forms.URLInput(attrs={"class": "form-control"}))
    pagination = forms.IntegerField(
        required=False, widget=forms.NumberInput(attrs={"class": "form-control"})
    )
    is_active = forms.BooleanField(
        required=False, widget=forms.CheckboxInput(attrs={"class": "form-check"})
    )

    class Meta:
        """Meta."""

        model = ProjectDetail
        fields = ["project", "url", "pagination", "is_active"]

live demo


Solution

  • Don't use project_form's request.POST for project_detail_form. Django thinks that you have already submitted the details form as well.

    def index(request):
        """Index view."""
        project_form = ProjectForm(data=request.POST or None)
        if request.method == "POST":
            try:
                if project_form.is_valid():
                    project_form.save()
                    return render(
                        request,
                        "monitor/_project_detail_form.html",
                        context={"project_detail_form": ProjectDetailForm()},
                    )
            except Exception as e:
                return HttpResponse(f"Error: {e}")
        return render(request, "monitor/index.html", context={"project_form": project_form})