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: .. code-block:: python 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"`` → ``Htmx4RequestHeaders`` - ``DX_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): .. code-block:: python @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:** .. list-table:: :header-rows: 1 :widths: 25 75 * - Property - Description * - ``is_hx_request`` - ``True`` when ``HX-Request: true`` * - ``request_type`` - Value of ``HX-Request-Type`` (``None`` if absent) * - ``current_url`` - Browser URL at the time of the request (``HX-Current-URL``) * - ``source`` - CSS selector of the element that triggered the request (``HX-Source``) * - ``target`` - CSS selector of the target element (``HX-Target``) * - ``is_boosted`` - ``True`` when the request was made via ``hx-boost`` * - ``is_history_nav`` - ``True`` for history-restore requests * - ``last_event_id`` - Value of ``Last-Event-ID`` (SSE resumption) **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**: .. code-block:: python @dx_put("item//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):** .. list-table:: :header-rows: 1 :widths: 35 65 * - Method - Effect * - ``set_trigger(payload)`` - ``HX-Trigger`` — fire a client-side event * - ``set_location(json_payload)`` - ``HX-Location`` — client-side AJAX redirect * - ``set_redirect(url)`` - ``HX-Redirect`` — full browser redirect * - ``set_refresh()`` - ``HX-Refresh: true`` — full page refresh * - ``set_retarget(selector)`` - ``HX-Retarget`` — override swap target * - ``set_reswap(swap_spec)`` - ``HX-Reswap`` — override swap method * - ``set_reselect(selector)`` - ``HX-Reselect`` — select a subset of the response to swap * - ``set_replace_url(url)`` - ``HX-Replace-Url`` — silently update the browser URL * - ``set_push_url(url)`` - ``HX-Push-Url`` — push a new entry onto the browser history 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: 1. **Header** (HTMX / JavaScript): ``X-HTTP-Method-Override: DELETE`` 2. **POST field** (plain HTML forms, no JavaScript required): ```` The header takes precedence when both are present. The field is only checked on ``POST`` requests. .. code-block:: html
{% csrf_token %}
Without middleware ------------------ If you do not enable the middleware, ``request.htmx`` will not be attached automatically. You can still create header helpers manually: .. code-block:: python from djxi.headers import Htmx4RequestHeaders, Htmx4ResponseHeaders def my_view(request): htmx = Htmx4RequestHeaders(request) if htmx.is_hx_request: response = HttpResponse("

partial

") Htmx4ResponseHeaders(response).set_trigger("done") return response return render(request, "full.html")