1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
Middleware
==========
.. currentmodule:: django_htmx.middleware
.. class:: HtmxMiddleware
This middleware attaches ``request.htmx``, an instance of :obj:`HtmxDetails` (below).
Your views, and any following middleware, can use ``request.htmx`` to switch behaviour for requests from htmx.
The middleware supports both sync and async modes.
See it action in the “Middleware Tester” section of the :doc:`example project <example_project>`.
.. admonition:: Set the ``Vary`` header for cacheable responses
If you set HTTP caching headers, ensure any views that switch content with ``request.htmx`` attributes add the appropriate htmx headers to the ``Vary`` header, per Django’s documentation section |Using Vary headers|__.
For example:
.. |Using Vary headers| replace:: Using ``Vary`` headers
__ https://docs.djangoproject.com/en/stable/topics/cache/#using-vary-headers
.. code-block:: python
from django.shortcuts import render
from django.views.decorators.cache import cache_control
from django.views.decorators.vary import vary_on_headers
@cache_control(max_age=300)
@vary_on_headers("HX-Request")
def my_view(request):
if request.htmx:
template_name = "partial.html"
else:
template_name = "complete.html"
return render(request, template_name, ...)
.. hint::
If you are type-checking your Django project, declare ``request.htmx`` as below in any custom ``HttpRequest`` classes, per `the pattern in django-stubs <https://github.com/typeddjango/django-stubs?tab=readme-ov-file#how-can-i-create-a-httprequest-thats-guaranteed-to-have-an-authenticated-user>`__.
.. code-block:: python
from django.http import HttpRequest as HttpRequestBase
from django_htmx.middleware import HtmxDetails
class HttpRequest(HttpRequestBase):
htmx: HtmxDetails
.. class:: HtmxDetails
This class provides shortcuts for reading the htmx-specific `request headers <https://htmx.org/reference/#request_headers>`__.
.. automethod:: __bool__
``True`` if the request was made with htmx, otherwise ``False``.
Detected by checking if the ``HX-Request`` header equals ``true``.
This method allows you to change content for requests made with htmx:
.. code-block:: python
from django.shortcuts import render
def my_view(request):
if request.htmx:
template_name = "partial.html"
else:
template_name = "complete.html"
return render(request, template_name, ...)
.. attribute:: boosted
:type: bool
``True`` if the request came from an element with the ``hx-boost`` attribute.
Detected by checking if the ``HX-Boosted`` header equals ``true``.
You can use this attribute to change behaviour for boosted requests:
.. code-block:: python
def my_view(request):
if request.htmx.boosted:
# do something special
...
return render(...)
.. attribute:: current_url
:type: str | None
The current URL in the browser that htmx made this request from, or ``None`` for non-htmx requests.
Based on the ``HX-Current-URL`` header.
.. attribute:: current_url_abs_path
:type: str | None
The absolute-path form of ``current_url``, that is the URL without scheme or netloc, or ``None`` for non-htmx requests.
This value will also be ``None`` if the scheme and netloc do not match the request.
This could happen if the request is cross-origin, or if Django is not configured correctly.
For example:
.. code-block:: pycon
>>> request.htmx.current_url
'https://example.com/dashboard/?year=2022'
>>> # assuming request.scheme and request.get_host() match:
>>> request.htmx.current_url_abs_path
'/dashboard/?year=2022'
This is useful for redirects:
.. code-block:: python
if not sudo_mode_active(request):
next_url = request.htmx.current_url_abs_path or ""
return HttpResponseClientRedirect(f"/activate-sudo/?next={next_url}")
.. attribute:: history_restore_request
:type: bool
``True`` if the request is for history restoration after a miss in the local history cache.
Detected by checking if the ``HX-History-Restore-Request`` header equals ``true``.
.. attribute:: prompt
:type: str | None
The user response to `hx-prompt <https://htmx.org/attributes/hx-prompt/>`__ if it was used, or ``None``.
.. attribute:: target
:type: str | None
The ``id`` of the target element if it exists, or ``None``.
Based on the ``HX-Target`` header.
.. attribute:: trigger
:type: str | None
The ``id`` of the triggered element if it exists, or ``None``.
Based on the ``HX-Trigger`` header.
.. attribute:: trigger_name
:type: str | None
The ``name`` of the triggered element if it exists, or ``None``.
Based on the ``HX-Trigger-Name`` header.
.. attribute:: triggering_event
:type: Any | None
The deserialized JSON representation of the event that triggered the request if it exists, or ``None``.
This header is set by the `event-header htmx extension <https://github.com/bigskysoftware/htmx-extensions/blob/main/src/event-header/README.md>`__, and contains details of the DOM event that triggered the request.
|