POST requests (or anything that isn’t GET or HEAD) require some care due to CSRF protection.
The simplest approach involves creating a form as normal and including the {%
csrf_token %}
template tag.
<h1>Make some monsters!</h1>
<form>
{% csrf_token %}
How many: <input type="number" name="howmany" value="1">
<button
hx-post="{% url 'post_form_endpoint' %}"
hx-trigger="click"
hx-target="#monsters-created"
hx-swap="beforeend"
>Make some!
</button>
</form>
In this case the whole of the form, including the CSRF token and other
parameters like howmany
, get submitted as part of the POST request, and you
will have no problems.
Note that the normal pattern of POST/redirect/GET, which is needed to avoid problems with page refresh and form re-submission, is not needed in this case as the POST request doesn’t return a full page.
However, in many cases it will be inconvenient to use a form, or you may want to
use controls that wouldn’t normally submit other values, such as links. An easy
solution is to put the token into custom htmx headers, by adding hx-headers
into the <body>
element, usually in a base.html
template:
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
Example view code, template, base template
If it is awkward to put this into the <body>
element in a base template (because you are using a base template that you can’t modify, for example), another technique that works is to add a <script>
somewhere in an overridden base template to set the hx-headers
attribute using Javascript, like this:
<script>
document.body.setAttribute('hx-headers', '{"X-CSRFToken": "{{ csrf_token }}"}');
</script>
There are other options, like hooking into htmx:beforeRequest and adding Javascript code described in the Django CSRF docs