htmx in Django by examples
Delete object in Django, with HTML dialog
This pattern for deleting an object from the UI uses an HTML dialog element to ask the user for confirmation.
Upon deletion, the user is redirected to the listing page.
Markup and JavaScript code
<button id="delete">Delete object</button>
<dialog id="dialog-confirm">
<article>
<header>
<h6>Are you sure you want to delete the thing?</h6>
</header>
<form hx-delete="{% url 'thing:delete' pk=object.pk %}"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<button formmethod="dialog">
Yes, delete
</button>
<button autofocus id="delete-dismiss">
No, take me back
</button>
</form>
</article>
</dialog>
<script>
const dialog = document.getElementById("dialog-confirm");
document.getElementById("delete").addEventListener("click", () => {
return dialog.showModal();
});
document.getElementById("delete-dismiss").addEventListener("click", () => {
return dialog.close();
});
</script>
When the "Delete object" button is clicked, we open the HTML <dialog>
element as a modal with dialog.showModal()
.
Inside the dialog, we have a form augmented with htmx to make a DELETE
request to the backend. In particular, we use two htmx attributes:
hx-delete
issues aDELETE
request to the specified URLhx-headers
to pass the CSRF Token to Django along the request
Inside the form we have two buttons: one for confirming the deletion, and another to dismiss the action:
<button formmethod="dialog">
Yes, delete
</button>
<button autofocus id="delete-dismiss">
No, take me back
</button>
In particular, the deletion button takes the formmethod
attribute set as dialog
. This makes the form close the modal without JavaScript.
Since the button is inside the form and is not marked as type="button"
, on click the default submit behavior applies, thus the form gets submitted, sending at the same time a DELETE
request to the Django view with htmx.
An example of a variation of this pattern, where the deletion is handled in a listing page with a button for each item can be found here.
The Django view
On the Django side, we can handle the deletion with a DeleteView
by overriding the delete
method to send an HX-Redirect
header alongside with the response:
class ThingDelete(DeleteView):
model = Thing
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
res = http.HttpResponse(status=204)
res["HX-Redirect"] = reverse_lazy("list")
return res
This header makes htmx redirect the client to the given URL, in this case the listing page.
Worth noting, django-htmx
has a HttpResponseClientRedirect that you can peruse as well.
Table-like listing: sorting and filtering
This pattern uses Django templates to generate the markup, and htmx to render partial templates for sorting and filtering.
See Single-pages without the single-page with django2-tables, django-filter, and htmx.
Further resources
- htmx
- django-htmx, a set of nice tools to integrate htmx into Django