HTML-first frontend interaction for Django, JSON-only server, and client-side DOM updates.
Project description
Djact v4.0
File-based reactive components for Django. No React, no Inertia — just Python classes and HTML directives.
Django renders the page normally. After that, all interactions happen via AJAX. Server returns JSON only. Client updates the DOM.
Features
- File-based components —
dj:component="home"auto-loadscomponents/home.py - Standard Django flow —
urls.py → views.py → render(template.html)stays unchanged - Python Component class — Clean, testable, standard Python
- Reactive directives —
dj:click,dj:submit,dj:model,dj:function,dj:state - Template expressions —
[[ count ]],[[ user.name ]](no Django{{ }}conflict) - Auto-discovery — Components found automatically from installed apps
- Zero JS to write — Everything is declarative
Installation
pip install djact
1. Add to INSTALLED_APPS
INSTALLED_APPS = [
# ...
"djact",
]
2. Include URLs
from django.urls import path, include
urlpatterns = [
path("", include("djact.urls")),
]
3. Enable Middleware
MIDDLEWARE = [
# ...
"djact.middleware.DjactAutoLoadMiddleware",
]
Quick Start
Step 1: Create a Component
myapp/
components/
__init__.py
counter.py ← dj:component="counter"
# myapp/components/counter.py
class Component:
def mount(self, request):
"""Called on first page load. Return initial state."""
return {"count": 0}
def increment(self, request, data):
"""Called via dj:click. Return state updates."""
return {"count": data["count"] + 1}
def decrement(self, request, data):
return {"count": data["count"] - 1}
Step 2: Create Template
# myapp/views.py
from django.shortcuts import render
def home(request):
return render(request, "home.html")
<!-- myapp/templates/home.html -->
<!DOCTYPE html>
<html>
<head><title>Counter</title></head>
<body>
<div dj:component="counter" dj:state="count=0">
<h1>Count: [[ count ]]</h1>
<button dj:click="increment">+1</button>
<button dj:click="decrement">-1</button>
</div>
</body>
</html>
That's it. No JavaScript needed.
Directives
| Directive | Type | Description |
|---|---|---|
dj:component="name" |
Setup | Links HTML to a Python component |
dj:state="key=val, ..." |
Setup | Initial client-side state |
dj:click="method" |
Event | Calls server method on click |
dj:click="method(arg)" |
Event | Calls with arguments |
dj:submit="method" |
Event | Calls server method on form submit |
dj:function="setState(...)" |
Event | Client-side state update (no server call) |
dj:model="field" |
Binding | Two-way data binding for inputs |
dj:for="item in list" |
Loop | Repeat element for each item |
dj:if="condition" |
Condition | Show/hide based on expression |
dj:empty="list" |
Condition | Show only if list is empty |
dj:paginate="list" |
Pagination | Auto pagination controls |
Component File Structure
Components are auto-discovered from installed apps:
myapp/
components/
__init__.py
home.py ← dj:component="home"
counter.py ← dj:component="counter"
admin/
__init__.py
dashboard.py ← dj:component="admin/dashboard"
users.py ← dj:component="admin/users"
Component Class
class Component:
def mount(self, request):
"""Required. Returns initial state dict."""
return {"items": [], "name": ""}
def any_method(self, request, data):
"""Called via dj:click or dj:submit. Returns state updates."""
return {"items": data["items"] + [data["name"]]}
def delete(self, request, data, item_id):
"""Supports extra arguments: dj:click="delete(item.id)" """
items = [i for i in data["items"] if i["id"] != item_id]
return {"items": items}
CRUD Example
# myapp/components/users.py
from django.contrib.auth.models import User
class Component:
def mount(self, request):
users = list(User.objects.values("id", "username", "email")[:50])
return {"users": users, "username": "", "email": "", "editing_id": None, "error": ""}
def save_user(self, request, data):
username = data.get("username", "")
if not username:
return {"error": "Username is required!"}
User.objects.create_user(username=username, email=data.get("email", ""))
users = list(User.objects.values("id", "username", "email")[:50])
return {"users": users, "username": "", "email": "", "error": ""}
def delete_user(self, request, data, user_id):
User.objects.filter(id=user_id).delete()
users = list(User.objects.values("id", "username", "email")[:50])
return {"users": users}
<div dj:component="users" dj:state="users=[], username='', email='', editing_id=null, error=''">
<div dj:if="error" style="color:red">[[ error ]]</div>
<form dj:submit="save_user">
<input dj:model="username" placeholder="Username">
<input dj:model="email" placeholder="Email">
<button type="submit">Save</button>
</form>
<table>
<tr dj:for="user in users">
<td>[[ user.username ]]</td>
<td>[[ user.email ]]</td>
<td><button dj:click="delete_user(user.id)">Delete</button></td>
</tr>
</table>
<p dj:empty="users">No users yet.</p>
</div>
Settings
| Setting | Default | Description |
|---|---|---|
DJACT_ENDPOINT_URL |
Auto-resolved | Override the AJAX endpoint URL |
DJACT_COMPONENTS_MODULE |
None | Explicit module path (e.g. "myapp.components") |
How It Works
- Django renders HTML normally (standard
urls.py → views.py → template) - Middleware detects
dj:componentin the response and injects CSRF + JS assets - Client JS (
auto.js) boots: parsesdj:state, callsmount()on server - Server loads
Componentclass viaimportlib, callsmount(), returns JSON - Client updates DOM using
[[ expression ]]interpolation - User interactions (
dj:click,dj:submit) send AJAX POST → server method → JSON → DOM update
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file djact-4.0.0.tar.gz.
File metadata
- Download URL: djact-4.0.0.tar.gz
- Upload date:
- Size: 20.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0cdaeb2218bf6195f15d47f0ceeb5d5e2ff87889bca4ec7d8b755bf9c7f7c41
|
|
| MD5 |
b764da4af7b67f9bcdefced107cb8e60
|
|
| BLAKE2b-256 |
f0c2dbf4d433f69c0f1ce50c56ee12e08ff2006d5c8824c7e6204ae4b6e17c92
|
File details
Details for the file djact-4.0.0-py3-none-any.whl.
File metadata
- Download URL: djact-4.0.0-py3-none-any.whl
- Upload date:
- Size: 21.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8de1d2cf2a76b54c7991da0baa86f4c8a2cb80aa3c3da57a081382b64aef711e
|
|
| MD5 |
4d5dcc9930fbc0579c8d38061067c645
|
|
| BLAKE2b-256 |
41359d8f055ef2bf4f5cd8c858836e2bc22d9bbb041f4ab5cad583c25372a59b
|