HTMX Middleware and Headers
djxi ships an optional but recommended middleware that attaches HTMX header helpers to every request and response.
Enabling the middleware
Add DjxiHeadersMiddleware to MIDDLEWARE, after Django’s session and
message middleware:
MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"djxi.middleware.DjxiHeadersMiddleware",
# ...
]
Reading request headers (request.htmx)
Once the middleware is active, every HttpRequest has a request.htmx
attribute. Its class depends on DX_HTMX_VERSION:
DX_HTMX_VERSION = "4"→Htmx4RequestHeadersDX_HTMX_VERSION = "2"→Htmx2RequestHeaders
The object evaluates as a boolean — True when the request was made by
HTMX (i.e. the HX-Request: true header is present):
@dx_get("items/")
def items(self, request):
if request.htmx:
# Partial render for HTMX swap
return self.render_section(request, "item-list", context)
# Full page render for direct navigation
return render(request, "items.html", context)
HTMX 4 request header properties:
Property |
Description |
|---|---|
|
|
|
Value of |
|
Browser URL at the time of the request ( |
|
CSS selector of the element that triggered the request ( |
|
CSS selector of the target element ( |
|
|
|
|
|
Value of |
HTMX 2 request header properties:
is_hx_request, is_boosted, is_history_nav, current_url,
prompt, target, trigger_id (alias trigger), trigger_name.
Setting response headers (response.htmx)
After calling render_section() (or any other response builder), the
returned HttpResponse has a response.htmx helper attached.
All setter methods return self so they can be chained:
@dx_put("item/<int:pk>/move", name="item-move")
def move(self, request, pk):
item = Item.objects.get(pk=pk)
context = {"item": item}
response = self.render_section(request, "item-row", context)
# Chain multiple header mutations in a single expression
response.htmx.set_trigger("itemMoved").set_retarget(f"#row-{pk}").set_reswap("outerHTML")
return response
Available setters (HTMX 4 and 2):
Method |
Effect |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HTMX 2 also provides set_trigger_after_settle(payload) and
set_trigger_after_swap(payload).
HTTP method override
DjxiHeadersMiddleware supports two mechanisms for overriding the HTTP
method, useful for PUT/PATCH/DELETE requests:
Header (HTMX / JavaScript):
X-HTTP-Method-Override: DELETEPOST field (plain HTML forms, no JavaScript required):
<input type="hidden" name="_method" value="DELETE">
The header takes precedence when both are present. The field is only
checked on POST requests.
<!-- Plain HTML form that issues a DELETE without JavaScript -->
<form method="post" action="{% url 'todo:delete' item.pk %}">
{% csrf_token %}
<input type="hidden" name="_method" value="DELETE">
<button type="submit">Delete</button>
</form>
Without middleware
If you do not enable the middleware, request.htmx will not be attached
automatically. You can still create header helpers manually:
from djxi.headers import Htmx4RequestHeaders, Htmx4ResponseHeaders
def my_view(request):
htmx = Htmx4RequestHeaders(request)
if htmx.is_hx_request:
response = HttpResponse("<p>partial</p>")
Htmx4ResponseHeaders(response).set_trigger("done")
return response
return render(request, "full.html")