Skip to main content
docs/okay-jack-far-side.png

Okayjack adds a few extra hx-* attributes which let you create conditional response rules (i.e. for success (200) and error (422) responses) in your Templates instead of your Views. This results in much simpler Views.

<form hx-patch="/contacts"
        hx-success-block="page.html#toast"
        hx-success-target="#toast-container"
        hx-error-block="page.html#form"
        hx-error-target="#contact-form">
def contacts(request, question_id):
        form = ContactForm(request.PATCH)
        if form.is_valid():
                form.save()
                return HxSuccessResponse(request)
        return HxErrorResponse(request, {'form': form})

The hx-success-* attributes are used when the response is HxSuccessResponse, and hx-error-* attributes for HxErrorResponse.

Okayjack provides a superset of htmx attributes. All the regular htmx attributes are left untouched and work as normal, and you can mix Okayjack and base htmx attributes as you wish. The extra attributes, combined with the Django Hx classes, just set htmx response headers which htmx then processes as it normally would.

For Django 5.x projects, Okayjack (via django-render-block) adds support for selecting parts (i.e. {% block ... %}) of a template for use in a response. This is similar to DTL {% partialdef ... %} in Django 6. Using partials/blocks is very useful when using htmx as it keeps the number of template files to a minimum.

And Okayjack adds PUT and PATCH support to Django as well because they’re nice to use 😁.

Table of contents

Requirements

(optional) django-render-block

Installation

  1. pip install django-htmx-okayjack if using Django 6, or pip install django-htmx-okayjack[block] if using Django 5.

  2. Add to settings.py:

    INSTALLED_APPS = [
            ...,
            'okayjack',
            ...,
    ]
    
    
    MIDDLEWARE = [
            ...,
            'okayjack.middleware.OkayjackMiddleware',
            ...,
    ]
  3. Import okayjack.http in your views.py to use the HttpResponse-like classes:

    from okayjack.http import HxSuccessResponse, HxErrorResponse
  4. Load the htmx extension in the template in the usual way - see https://htmx.org/attributes/hx-ext/.:

    <head>
            <script defer src="{% static 'okayjack/js/htmx.ext.okayjack.js' %}"></script>
    </head>
    
    <body hx-ext="okayjack>

htmx attributes

Okayjack supports all htmx response headers https://htmx.org/reference/#response_headers.

You can use any combination of:

  • hx-* attributes``

  • hx-success-*

  • hx-error-*

htmx will use the values of hx-* unless there is a hx-success-* or hx-error-* value (for a success or error response respectively).

The * in hx-success-* and hx-error-* attributes can be any of the following.

  • location

  • push-url

  • redirect

  • refresh

  • replace-url

  • swap

  • target

  • fire-after-receive

  • fire-after-settle

  • fire-after-swap

  • fire

  • block

  • partial

  • do-nothing

  • alert

refresh

This attribute supports two types of refresh.

  1. hx-refresh="true" will do the normal refresh that is achieved by setting the HX-Refresh response header to true - see https://htmx.org/reference/#response_headers.

  2. hx-refresh (without an attribute value) will call the view that handles the current url and returns a response that contains the html from that view (assumes a GET request to the view). When the response is received by htmx, the body element of the page is replaced with the body element from the response content.

This second refresh style acheives the same result as a normal refresh except it does not require the additional GET request that the normal method uses, and the content is swapped into the page which results in less/no page loading flicker.

If the attribute value is a path, that will be used to generate the html instead of the current page’s url.

e.g. hx-refresh="{% url 'a_different_view' arg1 etc %}"

Sometimes it’s useful to know whether the request is a refresh. A hx_refresh variable is automatically added to the request for this purpose.

e.g. {% if request.hx_refresh }<script>...</script>{% endif %}

fire-after-*

Use these attributes to specify events you want to fire when the response is returned. The event can be fired after receiving, swapping, or settling. The names are based on the response headers - see https://htmx.org/headers/hx-trigger/.

Note that hx-trigger is used for specifying which event “triggers” htmx to send a request to the server (i.e. the event that was fired that made htmx do something), whereas these attributes are for specifying which events should be fired when a response is returned.

fire

Shorthand for fire-after-receive

block

This is the path to a template and optional template block. Used to generate the HTML response.

hx-block="base/home.html#welcome_block"

Blocks are regular Django template blocks.

{% block welcome_block %}<p>I'm inside a block!</p>{% endblock }

Requires the optional django-render-block dependency.

partial

Works the same as block. Requires Django 6 or above.

hx-partial="base/home.html#welcome_partial"

Patials are regular Django DTL partials.

{% partialdef welcome_partial inline %}<p>I'm inside a block!</p>{% endpartialdef }

do-nothing

Returns a HttpResponse with a 204 (No Content) status code.

alert

Displays a JavaScript alert with the specified value.

E.g. hx-alert="Hello world!" is equivelant to executing alert("Hello world!") in JavaScript.

The alert is displayed afterSettle.

Django API

Main classes

HxSuccessResponse

Creates a ‘success’ HxResponse. The response will use hx-success-* and hx-* attributes specified in the template.

HxSuccessResponse(request[, context, block=None, partial=None, swap=None, target=None, fire-after-receive=None, fire_after_settle=None, fire_after_swap=None])

HxErrorResponse

Creates an ‘error’ HxResponse. The response will use hx-error-* and hx-* attributes specified in the template.

HxErrorResponse(request[, context, block=None, partial=None, swap=None, target=None, fire-after-receive=None, fire_after_settle=None, fire_after_swap=None])

HxResponse

Creates a response that uses hx-* attributes in the template.

At a minimum, it will automatically get the template/block/partial for the response from either the block / partial kwarg or the hx-block / hx-partial attribute used in the htmx request.

HxResponse(request[, context, block=None, partial=None, swap=None, target=None, fire-after-receive=None, fire_after_settle=None, fire_after_swap=None])

HxResponse(request, { 'form': form })

HxResponse(request, { 'form': form, fire-after-receive='fire-this-event-when-response-is-received'})

Additional handy classes

These are response classes for common htmx actions besides swapping new HTML into the page.

HxDoNothing

A HttpResponse that tells htmx to do nothing

HxDoNothing()

HxRedirect

A HttpResponse that tells htmx to do a client side redirect to the provided URL

HxRedirect(reverse('home'))

HxRefresh

A HttpResponse that tells htmx to refresh the page.

HxRefresh()

If a path is provided, instead of the usual refresh operation, the response will include the html of the path along with headers to tell htmx to replace the body element in the page with the body element in the response content. The net result is a page refresh without the second network request and without the usual page refresh screen flicker (as the content is being swapped in rather than being reloaded via a GET request).

In the following example, Django’s reverse (from django.urls) is used to create the path.

HxRefresh(request, reverse('add_client', args=[client.id]))

HxFire(fire=None, fire_after_receive=None, fire_after_swap=None, fire_after_settle=None)

A HttpResponse that tells htmx to fire (aka trigger) an event - and do nothing else. https://htmx.org/headers/hx-trigger/ The arg value is the name of the event to fire. If the event name is supplied as a positional argument, fire_after_received is used.

HxFire('close-dialog-box')

The value can also be a JSON string, which allows for firing multiple events and/or passing data for the event.

HxAlert(alert_text)

A HttpResponse that tells htmx to execute window.alert(alert_text) - and do nothing else.

BlockResponse(block)

Creates a TemplateResponse-like object using django-render-block to render a block in a template. It’s a light wrapper around django-render-block.

The format of block is template_path/template_name#block_name.

BlockResponse('base/home.html#welcome_block')

Extended example

Let’s say you have a basic contact form.

<form method="post" action="/contact">
        {{ form.as_div }}
        <button type='submit'>
</form>

To receive data on the server you have a basic View. It validates the Form, and returns a success or error page.

def contact(request):
        form = ContactForm(request.POST or None)
        if form.is_valid():
                form.save()
                return render(request, "contact_success.html", {'form': form})
        return render(request, "contact_error.html", {'form': form})

With htmx, you can return partials instead of full pages, and swap them into the page. So you might decide to add an extra section to swap a success message into - no page refresh required 🥳. The partial needs to be a separate file so it can be used as a template in the View.

{% include 'app/partials/success-message.html' %}
<form hx-post="/contact" hx-target="#success-message">
        {{ form.as_div }}
        <button type='submit'>
</form>

The View will need a little update to reference the partial.

def contact(request):
        form = ContactForm(request.POST or None)
        if form.is_valid():
                form.save()
                return render(request, "app/partials/contact_success.html", {'form': form})
        return = render(request, "contact_error.html", {'form': form})

This will work well for a successful form submission, but if there’s an error, it will refresh the page. It would be nicer to swap out the form with one that displays the errors instead - no page refresh. That standard htmx way to do this of course is in the View using Response headers https://htmx.org/reference/#response_headers.

def contact(request):
        form = ContactForm(request.POST or None)
        if form.is_valid():
                form.save()
                return render(request, "app/partials/contact_success.html", {'form': form})

        response = render(request, "app/partials/contact_error.html", {'form': form})
        response['HX-Retarget'] = 'form'
        return response

And that’s it for vanilla htmx. We now have a page which submits a form, and uses htmx to swap in the results depending upon whether it is valid or invalid.

One downside is that to understand the UI behaviour and see what html will be generated, we now need to read three templates (2 partials + the main page) plus the View. It’s not terrible but can get tedious.

With Okayjack, we move most/all of the UI behaviour to the template so it can be read all in one place.

{% block success_message %}
        <div id='success-message' class='alert success tick'></div>
{% endblock %}
<form
        hx-post="/contact"
        hx-success-target="#success-message"
        hx-success-block="contacts.html#success_message"
        hx-error-target="form"
        hx-error-block="contacts.html#contact_form">
                {% block contact_form %}
                        {{ form.as_div }}
                {% endblock %}
                <button type='submit'>
</form>

Because of the above, we can now simplify the view.

def contact(request):
        form = ContactForm(request.POST or None)
        if form.is_valid():
                form.save()
                return HxSuccessResponse(request)
        return HxErrorResponse(request, {'form': form})

The changes in the above example using Okayjack are:

  1. We reference {% block %} to get our partials rather than seperate files, which keeps the html in one place.

  2. We specify different target and block attributes for use with successful/valid or error/invalid requests.

  3. We have the one hx-post attribute still. There’s no need to specify it twice when it’s the same for both.

  4. Valid forms return HxSuccessResponse, invalid returns HxErrorResponse. Our View no longer needs to do Retargets, etc, and it doesn’t need to specify which partials/blocks to use, so it becomes simple enough that it can usually be ignored when reading templates.

Metadata

Release files for django-htmx-okayjack 5.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-htmx-okayjack 5.0.1
File Size Uploaded
django_htmx_okayjack-5.0.1.tar.gz 1.3 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-htmx-okayjack 5.0.1
File Interpreter ABI Platform
django_htmx_okayjack-5.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / django_htmx_okayjack-5.0.1.tar.gz

Download URL django_htmx_okayjack-5.0.1.tar.gz
Size 1.3 MB
Tags Source
SHA-256 checksum
How to use checksums
c76ffd486cbae16a943a371a2c525322a2e33a124b52e455d2b02e64561e9946
BLAKE2b-256 checksum
How to use checksums
4a1fc778576200285bda559e61c7794050a9e9ab053d06e7d99cdab647195d14
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.14

Release files / django_htmx_okayjack-5.0.1-py3-none-any.whl

Download URL django_htmx_okayjack-5.0.1-py3-none-any.whl
Size 16.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
018573d3e7bf71e3b9c7a8774ff0d3b2c9e4f998f941890919f30458ba843080
BLAKE2b-256 checksum
How to use checksums
1d747a83c8a13e70371dd202451ad449fc3625feed40e1945bb147d9a0352e99
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

5.0.1 This release

2 release files

5.0

2 release files

4.0.2

2 release files

4.0.1

2 release files

4.0

2 release files

3.0

2 release files

2.1

2 release files

2.0.2

2 release files

2.0.1

2 release files

2

2 release files

1.1

2 release files

1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page