django-htmx

repository·main·Indexed 24 days ago

https://github.com/adamchainz/django-htmx

Extensions for using Django with htmx, providing middleware and utilities to make integration more idiomatic. It includes HtmxMiddleware for detecting HTMX requests via request.htmx, specialized response classes like HttpResponseClientRedirect and HttpResponseClientRefresh, and template tags for loading htmx scripts and handling CSRF tokens.

Tokens
6.4K
Snippets
22
Records
42
Agent score
84%

What's inside django-htmx

  1. How the django-htmx extension script works

    main

    The django-htmx extension script is rendered by htmx_script or django_htmx_script when settings.DEBUG is True.

    By default, htmx discards response content when encountering HTTP errors. This extension adds an error handler that detects 400, 403, 404, and 500 status codes and replaces the page content with the response body. This allows you to see Django's default error pages (like the 404 or 500 debug pages) directly within the htmx request flow, making debugging significantly easier.

  2. Debug django-htmx examples in the browser

    main

    When exploring the example application, you can use browser developer tools to understand how HTMX is interacting with the server:

    • Console: Read the HTMX debug log in the browser's console to see event details.
    • Network Tab: Inspect the specific requests made by HTMX to see the payloads and responses.
    • Source/Templates: View the HTML source or templates to see embedded HTML comments used for debugging or identification.
    • Server Logic: Examine example/views.py to see how the Django views handle the HTMX requests.
  3. Optimize partial rendering by swapping the base template

    main

    If you do not want to use an external package, you can manually swap the base template used for inheritance based on whether the request is an htmx request.

    1. In the view: Determine a base_template variable (e.g., _base.html for full pages and _partial.html for htmx requests) and pass it into the context.
    2. In the template: Use the {% extends base_template %} tag to dynamically select the layout.

    This approach requires maintaining a minimal _partial.html that contains only the necessary structural elements (like the <main> tag) to wrap the content block.

    @require_GET
    def partial_rendering(request: HttpRequest) -> HttpResponse:
        if request.htmx:
            base_template = "_partial.html"
        else:
            base_template = "_base.html"
    
        return render(
            request,
            "page.html",
            {
                "base_template": base_template,
            },
        )
    {# page.html #}
    {% extends base_template %}
    
    {% block main %}
      ...
    {% endblock %}
  4. Make htmx pass Django's CSRF token

    main

    When using "unsafe" htmx methods like hx-post, you must ensure htmx sends Django's CSRF token in the x-csrftoken header. The most efficient way to do this is to add the hx-headers attribute to your <body> tag. This allows all htmx elements to inherit the token.

    Note: Use the {{ csrf_token }} template variable rather than the {% csrf_token %} template tag, as the tag renders a hidden input field which is not what hx-headers requires.

    <body hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'>
      ...
    </body>
  5. Configure django-htmx in Django settings

    main

    To use django-htmx, you must add it to your INSTALLED_APPS.

    Optionally, you can add django_htmx.middleware.HtmxMiddleware to your MIDDLEWARE setting. This middleware provides the request.htmx attribute on the request object, allowing you to inspect HTMX-specific request details in your views.

    INSTALLED_APPS = [
        ...,
        "django_htmx",
        ...,
    ]
    
    MIDDLEWARE = [
        ...,
        "django_htmx.middleware.HtmxMiddleware",
        ...,
    ]
  6. Optimize partial rendering with django-template-partials

    main

    To reduce server load and payload size, you can render only the specific part of a template that needs updating during an htmx request. Using the django-template-partials package, you can define reusable sections with the {% partialdef %} tag.

    1. Define the partial: Wrap the section in {% partialdef name inline %}. The inline argument ensures it still renders during a full page load.
    2. Update the view: In your Django view, check request.htmx. If true, append #partial-name to your template string.

    This allows you to use a single view and template for both full page loads and htmx partial updates.

    from django.shortcuts import render
    from example.models import Country
    
    
    def country_listing(request):
        template_name = "countries.html"
        if request.htmx:
            template_name += "#country-table"
    
        countries = Country.objects.all()
    
        return render(
            request,
            template_name,
            {
                "countries": countries,
            },
        )
  7. Configure type-checking for request.htmx

    main

    If you use type-checking (like django-stubs), you must declare the htmx attribute on your custom HttpRequest class so that the type checker recognizes the HtmxDetails object attached by the middleware.

    from django.http import HttpRequest as HttpRequestBase
    from django_htmx.middleware import HtmxDetails
    
    
    class HttpRequest(HttpRequestBase):
        htmx: HtmxDetails
  8. Install and use htmx extensions

    main

    django-htmx vendors the core htmx library via the {% htmx_script %} tag, but it does not include htmx extensions. To use extensions (like the WebSocket extension), you should download the extension scripts and serve them from your own static files rather than using a CDN.

    Steps to add an extension:

    1. Download the extension's .js file (e.g., using curl) into your project's static directory.
    2. Include the script tag in your base template, ensuring it is placed after the htmx script tag.

    Example using the WebSocket extension:

    curl -L https://unpkg.com/htmx-ext-ws/dist/ws.min.js -o example/static/htmx-ext-ws.min.js
    {% load django_htmx static %}
    <!doctype html>
    <html>
      <head>
        ...
        {% htmx_script %}
        <script src="{% static 'htmx-ext-ws.min.js' %}" defer></script>
      </head>
      <body>
        ...
      </body>
    </html>
  9. Set up base templates for django-htmx

    main

    To enable full functionality, update your base template (Django or Jinja2) with the following:

    1. Load the django_htmx template tags.
    2. Include the {% htmx_script %} tag in your <head> to load HTMX and the django-htmx extension script.
    3. Include the Django CSRF token in your <body> via the hx-headers attribute to ensure POST requests are authorized.

    Example for Django templates:

    {% load django_htmx %}
    <!doctype html>
    <html>
      <head>
        ...
        {% htmx_script %}
      </head>
      <body hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'>
        ...
      </body>
    </html>