Skip to content
djust/docs
Appearance
Mode
djust.org →
Browse documentation

API Reference · 15 entries · @

Decorator reference

Decorators turn ordinary methods into framework hooks. @event_handler is the one you reach for constantly — it exposes a method to dj-* events; the rest tune reactivity, client-side behaviour (@debounce, @cache, @optimistic), access control, and background execution.

Event handling

3 entries

Decorators that expose methods to events. @event_handler is required for any method a dj-* event calls; @action adds automatic pending/error/result tracking; @server_function turns a method into same-origin RPC the browser can call without a re-render.

@event_handler Mark a method as a WebSocket event handler.
Signature
@event_handler(params=None, description='', coerce_types=True, expose_api=False, serialize=None)
Effect
Registers a validated WS handler

The required decorator for any method bound to a dj-* event. It introspects the signature to coerce string params to typed kwargs (item_id: int gets "5" as 5). Pass expose_api=True to also mount an HTTP POST endpoint. Cannot be combined with @server_function.

Python Usage
@event_handler()
def search(self, value: str = "", **kwargs):
    self.results = Product.objects.filter(name__icontains=value)
@action Handler with auto pending / error / result state.
Signature
@action(description='', coerce_types=True)
Effect
Tracks pending/error/result automatically

Builds on @event_handler to track state in self._action_state[name]: pending=True on entry, result on success, error on exception (logged, not re-raised). Templates read {{ create_todo.pending }} etc. React-19-style server actions.

Python Action
@action()
def create_todo(self, title: str = "", **kwargs):
    if not title:
        raise ValueError("Title required")
    return {"id": Todo.objects.create(title=title).id}
HTML Template state
{% if create_todo.pending %}Creating…
{% elif create_todo.error %}{{ create_todo.error }}
{% elif create_todo.result %}Created #{{ create_todo.result.id }}{% endif %}
@server_function Same-origin browser RPC target — no re-render.
Signature
@server_function(description='', coerce_types=True)
Effect
Callable via djust.call(); JSON response

Call from JS as await djust.call('<view>', '<name>', {params}). Returns JSON with no VDOM re-render — ideal for autocomplete or lookups. Reuses param validation, @permission_required, and @rate_limit. Session-cookie auth + CSRF required. Cannot combine with @event_handler.

Python Server side
@server_function()
def search(self, q: str = "", **kwargs) -> list[dict]:
    return [{"id": p.id, "name": p.name}
            for p in Product.objects.filter(name__icontains=q)[:10]]
JavaScript Client call
const hits = await djust.call('app.ProductView', 'search', {q: 'laptop'});

Reactivity

3 entries

Declare derived and auto-tracking state. @state declares a reactive attribute, @reactive makes a property re-render on change, and @computed memoizes a value derived from dependencies — recomputing only on changes.

@reactive Property that re-renders when its value changes.
Signature
@reactive (with optional .setter)
Effect
Setter calls self.update() on change

A descriptor-based reactive property. When the value changes, it automatically queues a re-render via self.update(). Use the .setter form to add custom assignment logic.

Python Usage
class MyView(LiveView):
    @reactive
    def count(self):
        return self._count

    @count.setter
    def count(self, value):
        self._count = value  # re-renders on change
@state Declare reactive state with a default value.
Signature
name = state(default=None)
Effect
Class-level reactive attribute

A cleaner alternative to initialising attributes in mount(). The attribute is reactive (mutating it re-renders) and is included in the template context automatically.

Python Usage
class TodoView(LiveView):
    count = state(default=0)

    @event_handler()
    def increment(self, **kwargs):
        self.count += 1  # triggers re-render
@computed Derived property, optionally memoized on deps.
Signature
@computed or @computed('dep1', 'dep2')
Effect
Recomputes (or caches) a derived value

Plain @computed recomputes on every access (cheap derivations). The memoized form @computed('items', 'tax_rate') caches until the named dependencies change (by fingerprint). Thread-safe.

Python Plain
@computed
def doubled(self):
    return self.count * 2
Python Memoized
@computed('items', 'tax_rate')
def total(self):
    sub = sum(i['price'] * i['qty'] for i in self.items)
    return sub * (1 + self.tax_rate)

Client-side behavior

5 entries

Tune how events behave on the client before reaching the server. @debounce and @throttle rate-limit chatty events; @optimistic updates the UI instantly and reconciles later; @cache stores responses with a TTL; @client_state shares state across components via the client StateBus.

@debounce Wait for a pause before sending the event.
Signature
@debounce(wait=0.3, max_wait=None)
Effect
Client waits idle period before dispatch

Client-side metadata: after an event fires the client waits wait seconds; a new event resets the timer. max_wait forces a dispatch if the idle period keeps extending. Ideal for live search inputs.

Python Search
@debounce(wait=0.5)
@event_handler()
def search(self, value: str = "", **kwargs):
    self.results = Product.objects.filter(name__icontains=value)
@throttle Limit handler to at most once per interval.
Signature
@throttle(interval=0.1, leading=True, trailing=True)
Effect
Client caps dispatch frequency

For scroll / resize / mousemove. leading fires immediately on the first event; trailing fires once more after the interval if events kept arriving. Both can be combined.

Python Scroll
@throttle(interval=0.1)
@event_handler()
def on_scroll(self, scroll_y: int = 0, **kwargs):
    self.scroll_position = scroll_y
@optimistic Apply the UI update instantly, correct on reply.
Signature
@optimistic
Effect
Client updates UI before the round-trip

Signals the client to apply the event optimistically for zero-latency feel; the server's response patches any mismatch. Best for toggles and counters where the result is predictable.

Python Usage
@optimistic
@event_handler()
def increment(self, **kwargs):
    self.count += 1
@cache Cache handler responses client-side with a TTL.
Signature
@cache(ttl=60, key_params=None)
Effect
Client caches the response for ttl seconds

Client-side cache keyed by handler name plus the named key_params. With key_params=['query'] the key is search:laptop. Omit key_params for a handler-name-only cache. Instant repeat results.

Python Usage
@cache(ttl=60, key_params=['query'])
@event_handler()
def search(self, query: str = "", **kwargs):
    self.results = Product.objects.filter(name__icontains=query)
@client_state Share state across handlers via a client StateBus.
Signature
@client_state(keys=['tab'])
Effect
Publishes keys on the client StateBus

Enables cross-component coordination without a server round-trip: when the handler runs, the named keys are published on the client and other subscribed handlers are notified. Pub/sub on the client.

Python Usage
@client_state(keys=['tab'])
@event_handler()
def switch_tab(self, tab: str = "", **kwargs):
    self.tab = tab

Access & execution

4 entries

Gate and schedule handler execution. @permission_required and @rate_limit enforce access and throughput server-side; @background runs a handler after flushing the UI so slow work doesn't block the response; @on_mount registers cross-cutting logic before mount().

@permission_required Require Django permission(s) to run the handler.
Signature
@permission_required(perm)
Effect
Rejects the event if the user lacks perm

Checked server-side before the handler runs. Accepts a single permission string or a list. Composes with @event_handler / @server_function and the @rate_limit token bucket.

Python Usage
@permission_required('myapp.delete_item')
@event_handler()
def delete_item(self, item_id: int = 0, **kwargs):
    Item.objects.filter(id=item_id).delete()
@rate_limit Token-bucket rate-limit a handler server-side.
Signature
@rate_limit(rate=10, burst=5)
Effect
Drops events past the limit + warns client

Per-handler token bucket: rate is sustained events/sec, burst is the bucket capacity. When exceeded the event is dropped and the client is warned. Protects expensive handlers.

Python Usage
@rate_limit(rate=5, burst=3)
@event_handler()
def expensive(self, **kwargs):
    self.results = do_expensive_work()
@background Run the handler in the background after flushing.
Signature
@background
Effect
Flushes UI, then runs async

Wraps start_async(): state set before the heavy work (e.g. self.loading=True) flushes to the client immediately, then the body runs in the background and re-renders on completion. Works with sync and async handlers.

Python Usage
@event_handler()
@background
def generate(self, prompt: str = "", **kwargs):
    self.generating = True
    self.content = call_llm(prompt)   # slow
    self.generating = False
@on_mount Cross-cutting hook that runs before mount().
Signature
@on_mount def hook(view, request, **kwargs)
Effect
Runs each mount; may redirect

Return None to continue or a URL string to halt the mount and navigate away. Hooks run in MRO order, parent-first, deduplicated. Equivalent to Phoenix's on_mount/1. Authentication runs before hooks.

Python Usage
@on_mount
def require_verified(view, request, **kwargs):
    if not request.user.email_verified:
        return '/verify-email/'

class ProfileView(LiveView):
    on_mount = [require_verified]