weft-django
weft-django is the first-party Django integration for Weft.
The package is typed (py.typed) and depends on Weft through the public
weft.client API.
It provides:
@weft_taskfor Django-owned synchronous background functions- transaction-safe submission helpers such as
enqueue_on_commit() - native TaskSpec, stored spec, and pipeline submission helpers
- read-only Django URLs for task inspection
- SSE by default, with optional Channels/WebSocket transport
- Django management commands for task status and control
Install:
uv add weft-django
Or from the main package convenience extra:
uv add "weft[django]"
Install the optional Channels transport with:
uv add "weft-django[channels]"
Equivalent install surfaces:
uv add "weft-django[realtime]"
uv add "weft[django-channels]"
Basic usage:
from weft_django import weft_task
@weft_task(name="billing.send_invoice", timeout=60)
def send_invoice(invoice_id: int) -> dict[str, int]:
return {"invoice_id": invoice_id}
submission = send_invoice.enqueue(123)
result = submission.result(timeout=30)
assert result.status == "completed"
Project Context
Django requests its runtime context from Weft. With no explicit context setting,
Weft discovers the nearest project starting at Django's BASE_DIR, using that
directory itself when discovery finds nothing:
# settings.py
INSTALLED_APPS += ["weft_django"]
WEFT_DJANGO = {}
To pin a project explicitly, set WEFT_DJANGO = {"CONTEXT": BASE_DIR}. The explicit
Django setting wins over WEFT_CONTEXT. Otherwise WEFT_CONTEXT selects the root
before discovery from BASE_DIR. Without BASE_DIR, Weft uses its ordinary CWD
discovery. Explicit roots are used directly; they are not discovery anchors.
Broker settings such as WEFT_BACKEND_TARGET select the broker while preserving
this project-root policy. For example, a PostgreSQL target does not move Weft's
artifact directory to the web worker's CWD. Project broker configuration retains
its existing precedence. BROKER_* variables do not configure Weft.
Settings import and task discovery do not initialize Weft. Runtime operations acquire a client with a resolved context and Config snapshot; a retained client keeps that snapshot, while a later acquisition can observe new settings.
After upgrading, stray BROKER_* or default-valued broker settings no longer
redirect Django's artifacts to CWD. If an installation used that former
destination, pin CONTEXT to its existing root before upgrading. The integration
does not move existing tasks or artifacts. Restart long-lived Django processes
to pick up the new code and settings.
Submission Handle
enqueue(...) and the native submission helpers return WeftSubmission.
The handle exposes:
tidnamestatus()wait(timeout=None)result(timeout=None)stop()kill()events(follow=False)
status() returns the current public status string or None if no snapshot is
available yet. wait() and result() both return the structured Weft
TaskResult.
Module-level status(tid) and terminal_snapshot(tid) use Weft's compact
known-TID terminal snapshot path. They are read-only and can report terminal
Monitor-store fallback after raw task-log rows retire. Use snapshot(tid) when
callers need diagnostic fields such as task metadata, runtime details, or
timestamps.
enqueue_on_commit(...) and the native *_on_commit(...) helpers return
WeftDeferredSubmission. The deferred handle has a stable name immediately
and gains tid plus task methods after the outer transaction commits. Calling
result-like methods before commit raises a local RuntimeError.
A successful Weft broker write binds the deferred TID even if manager readiness then degrades. That readiness warning does not raise from the commit callback or stop later callbacks. Broker-write failure and authoritative manager rejection still raise. The hook is not a durable outbox or an atomic cross-database write.
Deferred helpers validate and snapshot before registering Django's
transaction.on_commit() callback. Missing spec references, invalid overrides,
and unserializable payloads fail before the app transaction commits. Mutating
args, kwargs, or payload objects after helper call time does not change the work
submitted at commit.
The helpers also capture the core client before registering the callback. Changing Django settings, environment, CWD, or HOME before commit does not redirect prepared work. Core preparation binds an explicit relative or home-relative TaskSpec context to its absolute path. An explicitly different TaskSpec root still has its broker selected at submission using captured Config; broker project files are not snapshotted.
Composition Export
task.as_taskspec_for_call(*args, _overrides=None, **kwargs) returns the
validated, normalized TaskSpec definition for that call, with the
call envelope embedded in spec.args, for manual composition into ordinary Weft
task or pipeline specs. It does not submit anything, builds no Weft context,
reads no Weft configuration, opens no broker, and writes nothing (the configured
REQUEST_ID_PROVIDER still runs, as it does for every call).
_overrides accepts exactly Weft's public submit overrides (name,
description, tags, env, working_dir, stream_output, timeout,
memory_mb, cpu_percent, runner, runner_options, metadata) with core
semantics: None values are ignored, unknown names (including wait) raise
TypeError, and invalid values raise the TaskSpec validation error. The export
is weft.client.normalize_taskspec_payload(...) applied to the generated
template; the package applies no overrides of its own.
An explicit Django CONTEXT is copied into spec.weft_context as declared,
including relative or home-relative text. With no explicit setting, the field
remains unset: the export does not capture BASE_DIR, environment, or a
discovered project. Such exports inherit their destination from the receiving
Weft context when submitted or composed. This makes exports portable; set
CONTEXT explicitly when the declaration must name a particular project.
Native Helpers
Use these helpers when Django code wants to launch native Weft work instead of a decorated Django function:
from pathlib import Path
from weft_django import (
submit_pipeline_reference,
submit_spec_reference,
submit_taskspec,
)
task = submit_taskspec(taskspec, payload={"job": 1})
task = submit_spec_reference(Path(".weft/tasks/report.json"), payload={"job": 1})
task = submit_pipeline_reference("nightly-report", payload={"job": 1})
Keyword rules:
- native helpers use
payload=... work_payload=...andinput=...are intentionally not supported- deferred helpers reject
wait=True
Testing
weft-django does not ship an eager or inline execution mode.
Use the direct Python callable for narrow unit tests:
assert send_invoice(123)["invoice_id"] == 123
Use broker-backed tests for enqueue behavior, transaction hooks, process boundaries, streaming, native TaskSpecs, bundles, agents, and pipelines.
Realtime And URLs
Include the read-only URLs explicitly:
from django.urls import include, path
urlpatterns = [
path("weft/", include("weft_django.urls")),
]
You must configure an authz callable:
WEFT_DJANGO = {
"AUTHZ": "myapp.weft_authz:authorize",
}
Supported realtime settings:
WEFT_DJANGO = {
"REALTIME": {
"TRANSPORT": "sse", # "none" | "sse" | "channels"
},
}
Notes:
GET /weft/tasks/<tid>/returns the current task snapshotGET /weft/tasks/<tid>/events/is the SSE endpoint whenTRANSPORT="sse"TRANSPORT="none"disables the SSE endpointTRANSPORT="channels"switches browser realtime delivery to the optional WebSocket consumer inweft_django.channels- the Channels consumer starts a cancellable background stream after socket accept rather than blocking the connect lifecycle
- the HTTP and realtime surfaces are diagnostics only; they do not create a second task-truth store
Celery Migration Guide
| Celery habit | weft-django |
|---|---|
@shared_task |
@weft_task |
task.delay(...) |
task.enqueue(...) |
transaction.on_commit(lambda: task.delay(...)) |
task.enqueue_on_commit(...) |
AsyncResult |
WeftSubmission |
delay and shared_task are intentionally not shipped. They are close enough
to invite mechanical porting and far enough from Weft semantics to create
delayed failures.
Release files for weft-django 0.9.39
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| weft_django-0.9.39.tar.gz | 17.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| weft_django-0.9.39-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 45.6 kB
Release files / weft_django-0.9.39.tar.gz
| Download URL | weft_django-0.9.39.tar.gz |
|---|---|
| Size | 17.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
770e73119b16bd475209687235013831ee544745016a35c6b7ef69c5f2f3365e
|
|
BLAKE2b-256 checksum How to use checksums |
e604458ffffbd8fb65b68c649942e79efc937339fd9cc2cce075fe4fc702e9eb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / weft_django-0.9.39-py3-none-any.whl
| Download URL | weft_django-0.9.39-py3-none-any.whl |
|---|---|
| Size | 28.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7fbaac0c982094f87631fda573430814100ab8f8b2b702457f2d018172c3c2fd
|
|
BLAKE2b-256 checksum How to use checksums |
26f03639e45b4d9fafc2fcc39da208ff3bb5b47138e691969d05ad9918ad2c23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|