I've got an application where I'm listing out a bunch of forms - it loads a csrf_token into each form.
Each form is a simple dropdown for choosing a 'color' for each item in the list.
I have a ListView
that returns a list of placement objects like so:
{% for placement in placements %}
{% include 'placements/snippets/placement_update_form.html' %}
{% endfor %}
The placement_update_form.html
looks like this:
<form id="placementUpdateForm" action="{{ placement.get_update_url }}" method="post" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<input type="hidden" name="id" value="{{ placement.id }}">
<label for="color">Color</label>
<select name="color" id="color{{ placement.pk }}" class="form-select" hx-post="{% url 'placements:placement-update' placement.pk %}" hx-target="closest #placementUpdateForm">
<option value="" {% if not placement.color %}selected{% endif %}>Default</option>
{% for value, display_name in placement_colors %}
<option value="{{ value }}" {% if value == placement.color %}selected{% endif %}>{{ display_name }}</option>
{% endfor %}
</select>
</form>
I am using HTMX to POST
the data to an UpdateView
on my backend; which redirects to a success_url
that is a DetailView
that simply returns the placement_update_template
and replaces that exact item in the DOM.
It works - once. The first POST
works and saves the color as expected. The template is replaced in the DOM, but if you choose a different color (triggering a second POST
) then it fails.
Also, if I try to change the color of any of the other forms - it also fails with the same error.
All subsequent POST
requests are giving me this error:
Forbidden (CSRF token from the 'X-Csrftoken' HTTP header incorrect.): /placements/1/update/
What is the best way to handle this when submitting these forms via HTMX?
Here is a more in-depth breakdown of views/templates/logic: https://pastebin.com/YttsAYZC
This is a common issue and the easiest way to handle it is to use hx-headers in the following way:
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
...
</body>
It’s most convenient to place
hx-headers
on your<body>
tag, as then all elements will inherit it.
Ref: https://django-htmx.readthedocs.io/en/latest/tips.html#make-htmx-pass-the-csrf-token