Tutorial: Your first DXEndpointBattery
This tutorial walks you through building a minimal but complete HTMX-powered feature with djxi. By the end you will have a battery that lists, creates, and deletes items — with URL routing, template sections, and Django messages all wired up.
Prerequisites
djxi installed and added to
INSTALLED_APPS(see Installation and Setup)DjxiHeadersMiddlewareinMIDDLEWAREA base template using
{% load djxi %}/{% htmx_headers %}
Step 1 — Define the template sections
A battery’s template is split into named <dx-section> blocks. You can
use either an inline string (inline_template) or a file on disk
(template_name). Both support full Django template syntax.
INLINE_TEMPLATE = """
<dx-section name="item-list-container">
<div id="item-list-container">
<h3>Items</h3>
<dx-include name="item-list"/>
<dx-include name="item-form"/>
</div>
</dx-section>
<dx-section name="item-list">
<ul id="item-list">
{% for item in items %}
<li id="item-{{ item.pk }}">
{{ item.title }}
<button hx-delete="{% url 'items:delete' item.pk %}"
hx-target="#item-{{ item.pk }}"
hx-swap="outerHTML">×</button>
</li>
{% empty %}
<li>No items yet.</li>
{% endfor %}
</ul>
</dx-section>
<dx-section name="item-form">
<form hx-post="{% url 'items:create' %}"
hx-target="#item-list"
hx-swap="outerHTML">
{% csrf_token %}
<input name="title" placeholder="New item" required>
<button type="submit">Add</button>
</form>
</dx-section>
"""
<dx-include name="..."/> expands the named section inline at render time,
so you can compose larger sections from smaller reusable ones.
Step 2 — Create the battery class
# myapp/views.py
from django.contrib import messages
from djxi import DXEndpointBattery, dx_get, dx_post, dx_delete
from .models import Item
class ItemBattery(DXEndpointBattery):
inline_template = INLINE_TEMPLATE
battery_prefix = "htmx" # → /items/htmx/…
# ── helpers ──────────────────────────────────────────────────────
def _items_context(self):
return {"items": Item.objects.order_by("-pk")}
# ── endpoints ─────────────────────────────────────────────────────
@dx_get("list", name="list")
def list(self, request):
return self.render_section(request, "item-list-container", self._items_context())
@dx_post("create", name="create")
def create(self, request):
title = request.POST.get("title", "").strip()
if title:
Item.objects.create(title=title)
messages.success(request, f"'{title}' added.")
return self.render_section(request, "item-list", self._items_context())
@dx_delete("item/<int:pk>/delete", name="delete")
def delete(self, request, pk):
Item.objects.filter(pk=pk).delete()
messages.info(request, "Item removed.")
return self.render_empty(request)
Key points:
render_section(request, name, context)renders the named section and returns anHttpResponse.render_empty(request)returns an emptyHttpResponse, used here to replace the deleted list item with nothing.messages.success(...)is automatically injected into the next HTMX response as an out-of-band swap.
Step 3 — Wire up the URLs
# myapp/urls.py
from django.urls import path, include
from .views import ItemBattery
app_name = "items"
urlpatterns = [
path("", include(ItemBattery.url_patterns())),
# battery_prefix = "htmx" → registers:
# GET /htmx/list
# POST /htmx/create
# DELETE /htmx/item/<pk>/delete
]
# project/urls.py
urlpatterns = [
path("items/", include("myapp.urls")),
]
Step 4 — Add the list to a page template
{# items/index.html #}
{% extends "base.html" %}
{% block content %}
{# Bootstrap the list on page load — will be swapped in-place by HTMX #}
<div hx-get="{% url 'items:list' %}" hx-trigger="load"></div>
{% endblock %}
Step 5 — Protecting endpoints (optional)
To restrict the battery to authenticated users, add requires_auth = True:
class ItemBattery(DXEndpointBattery):
inline_template = INLINE_TEMPLATE
battery_prefix = "htmx"
requires_auth = True # 403 for anonymous users
login_url = "/accounts/login/" # redirect instead of 403
See Permissions and Access Control for fine-grained access control.
Step 6 — Verify routes
python manage.py djxi_routes
Battery URL Pattern Methods Name
─────────────────────────────────────────────────────────
ItemBattery htmx/list GET list
ItemBattery htmx/create POST create
ItemBattery htmx/item/<pk>/delete DELETE delete
See Management Command: djxi_routes for full command reference.
Further reading
HTMX Middleware and Headers — reading and setting HTMX headers
Permissions and Access Control —
requires_auth,login_url,check_permissionsAsync Battery Support — writing
async defhandlersTesting Batteries — testing batteries with
DXBatteryTestCaseThe full
todoexample app inexample/todo/views.py