HTMX Form in Django: Errors not updating in the DOM after failed validation

I am using HTMX with Django to submit a contact form without refreshing the page. When the form has errors, my backend correctly returns the contact_messages.html template, but the errors do not appear in the page after the request.

I can see the correct response in the Network tab, and contact_messages.html contains the expected validation messages. However, HTMX does not update the div#contact-messages in my contact.html file.

Before trying to use HTMX, Django's built-in form validations work perfectly. However, as everyone knows, the process is synchronous, and I want to make it asynchronous with HTMX.

This's mi view:

class ContactView(FormView):

    template_name = 'contact/contact.html'
    form_class = ContactForm
    success_url = reverse_lazy('contact')

    def form_valid(self, form):        

        messages.success(self.request, "Your message has been sent. We will be in touch with you as soon as possible.")
        return redirect(self.success_url + '#contact-section')


    def form_invalid(self, form):
        messages.error(
            self.request,
            "There were some errors in your submission. Please review the highlighted fields and try again",
            extra_tags="danger")

        if self.request.headers.get("HX-Request"):
            return render(self.request, "contact/contact_messages.html", {"form": form}, content_type="text/html", status=400)

        return super().form_invalid(form)

This's my form in contact.html:

<form id="contact-form" method="post" hx-post="{% url 'contact' %}"
                         hx-target="#contact-messages"  hx-swap="innerHTML" novalidate>

                        {% csrf_token %}

                        <div id='contact-messages'>
                            {% include 'contact/contact_messages.html' %}

                        </div>


                        <div class="form-group mb-0">
                            <button class=" btn btn-primary w-100">
                                <span>Send Message</span>
                            </button>
                        </div>

                       
                    </form>

This's my secondary template contact_messages.html:

% if messages %}
    {% for message in messages %}
        <div class="alert alert-{{ message.tags }}" role="alert">
            {{ message }}
            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                <span aria-hidden="true">&times;</span></button>
        </div>
    {% endfor %}
{% endif %}

{% for field in form %}
    <div class="form-group">
        {{ field }}
        {% if form.errors %}
            <div class="invalid-feedback d-block">
                {% for error in field.errors %}
                    {{ error }}
                {% endfor %}
            </div>
        {% endif %}
    </div>
{% endfor %}

Here you can see that Django returns the correct response, but I can't get it to be reflected in contact.html.

enter image description here

By design HTMX does not swap content when error response is returned (4xx).

Here's one way to go around this:

https://gist.github.com/lysender/a36143c002a84ed2c166bf7567b1a913

Another way is to simply return 200 instead of 400.

Вернуться на верх