Search code examples
djangoformsdjango-templatesresponsehtmx

Django-HTMX: on form validation error render to diferent target


I'm starting to mess around with HTMX and found a wall I haven't been able to climb on my own:

I used HTMX to manage the creation/edition form and post the data to the corresponding view and update the item list. It works marvelously. But when the form raises Validation Errors the list is replaced by the form html.

Is there a way to set a different render target for when the form has validation errors?

<div class="modal-header">
    <h5 class="modal-title" id="Object_ModalLabel">{% if object %}Edit{% else %}Create{% endif %} Object</h5>
    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
    <form hx-post="{{targetURL}}" hx-target="#Object_List" method="POST">
    {% csrf_token %}
        {{form.as_div}}
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
            <button type="submit" class="btn btn-primary">Save</button>
        </div>
    </form>
</div>
from django.views.generic import TemplateView, ListView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from project.mixins import HtmxRequiredMixin, ResponsePathMixin
from .models import Object_
from .forms import Object_Form
# Create your views here.

class Object_Main(TemplateView):
    template_name = 'Main.html'

class Object_List(HtmxRequiredMixin, ListView):
    model = Object_
    template_name = 'Object_List.html'
    queryset = Object_.objects.all().order_by('as_group','as_name')

class Object_Create(ResponsePathMixin, HtmxRequiredMixin, CreateView):
    template_name = 'Object_Form.html'
    model = Object_
    form_class = Object_Form
    success_url = reverse_lazy('list-object')

class Object_Update(ResponsePathMixin, HtmxRequiredMixin, UpdateView):
    template_name = 'Object_Form.html'
    model = Object_
    form_class = Object_Form
    success_url = reverse_lazy('list-object')

Those are the template where the form is placed and my views. If there is no way to set different targets based on the response then I would thank any suggestion.

I checked different sources but none were approaching the issue of form validation from the same angle. I don't need real time validation right now.

I asked Bing for some insight but was unable to find a satisfying/let alone working solution. So I thought to ask for help among my fellow human programmers.


Solution

  • You can do that by modyfing your response header using HX-Retarget. In case of CBV you can override form_invalid method:

    def form_invalid(self, form):
        """If the form is invalid, render the invalid form."""
        reponse = self.render_to_response(self.get_context_data(form=form))
        response['HX-Retarget'] = '#other-id"
        return response
    

    Ref: https://htmx.org/reference/#response_headers


    There's also quicker, if you're using django-htmx:

    https://django-htmx.readthedocs.io/en/latest/http.html#django_htmx.http.retarget

    Just a side note. When dealing with htmx FBV approach is much better then CBV, so you might consider switching, to avoid all these method overriding.