packages/
mizan-runtime/ Framework-agnostic state engine (~150 lines)
Context registry, batched invalidation, fetch primitives
mizan-django/ Django server adapter (was packages/mizan-rpc/adapters/django/)
Codegen moved to mizan-django/generate/
mizan-react/ React adapter (was packages/mizan-csr/adapters/react/)
Removed premature abstractions: mizan-ast, mizan-schema, mizan-rpc,
mizan-csr, mizan-ssr stub packages. The actual architecture is three
concrete packages, not five abstract layers.
mizan-runtime implements the v1 spec: registerContext with params,
scoped invalidation via microtask batching, server-driven invalidation
from mutation responses, mizanFetch for context bundles, mizanCall for
mutations.
264 Django + 33 React tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
106 lines
2.1 KiB
Markdown
106 lines
2.1 KiB
Markdown
# mizan (Python)
|
|
|
|
Django server functions framework. See the [monorepo root](../README.md) for full documentation.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
uv add "mizan[channels,allauth] @ git+https://git.impactsoundworks.com/isw/mizan.git#subdirectory=django"
|
|
```
|
|
|
|
## Setup
|
|
|
|
```python
|
|
# settings.py
|
|
INSTALLED_APPS = ["mizan", ...]
|
|
|
|
# urls.py
|
|
path("api/mizan/", include("mizan.urls"))
|
|
|
|
# asgi.py (optional, for WebSocket)
|
|
from mizan import wrap_asgi
|
|
application = wrap_asgi(get_asgi_application())
|
|
```
|
|
|
|
## Define Functions
|
|
|
|
```python
|
|
from mizan.client import client
|
|
from mizan.setup.registry import register
|
|
from pydantic import BaseModel
|
|
|
|
class Output(BaseModel):
|
|
message: str
|
|
|
|
@client
|
|
def echo(request, text: str) -> Output:
|
|
return Output(message=text)
|
|
|
|
register(echo, "echo")
|
|
```
|
|
|
|
Register in `apps.py`:
|
|
|
|
```python
|
|
def ready(self):
|
|
import myapp.mizan_clients
|
|
```
|
|
|
|
## Auth
|
|
|
|
```python
|
|
@client(auth=True) # requires authentication
|
|
@client(auth='staff') # requires is_staff
|
|
@client(auth='superuser') # requires is_superuser
|
|
@client(auth=my_callable) # custom check
|
|
```
|
|
|
|
## Contexts
|
|
|
|
```python
|
|
@client(context='global') # fetched once, SSR-hydrated, becomes useCurrentUser()
|
|
@client(context='local') # fetched with params, becomes <GreetProvider>
|
|
```
|
|
|
|
## Forms
|
|
|
|
```python
|
|
from mizan.forms import mizanFormMixin, mizanFormMeta
|
|
|
|
class ContactForm(mizanFormMixin, forms.Form):
|
|
mizan = mizanFormMeta(name="contact", title="Contact Us")
|
|
name = forms.CharField()
|
|
email = forms.EmailField()
|
|
|
|
def on_submit_success(self, request):
|
|
return {"sent": True}
|
|
```
|
|
|
|
Auto-registers `contact.schema`, `contact.validate`, `contact.submit`. Generates `useContactForm()` with Zod validation.
|
|
|
|
## Channels
|
|
|
|
```python
|
|
from mizan.channels import ReactChannel
|
|
|
|
class ChatChannel(ReactChannel):
|
|
class Params(BaseModel):
|
|
room: str
|
|
class DjangoMessage(BaseModel):
|
|
text: str
|
|
|
|
def authorize(self, params):
|
|
return self.user.is_authenticated
|
|
def group(self, params):
|
|
return f"chat_{params.room}"
|
|
```
|
|
|
|
Generates `useChatChannel({ room })`.
|
|
|
|
## Running Tests
|
|
|
|
```bash
|
|
uv sync --extra dev --extra channels
|
|
uv run pytest
|
|
```
|