Skip to main content

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.

Supported Django versions are 6.1.2 and later 6.x releases (django>=6.1.2,<7).

It provides:

  • @weft_task for 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.

During a synchronous Django request, module-level submission and task helpers lazily share one request-owned DjangoWeftClient. The client retains a SimpleBroker session lease, not a checked-out database connection. Each broker operation still checks out, commits or rolls back, and returns its connection before manager readiness or application code continues. Request start and finish do no broker work when no helper is used.

This automatic lifetime covers WSGI requests and synchronous views running in Django's thread-sensitive ASGI executor. A helper called directly from an async view uses a bounded one-shot client. A synchronous streaming response retains the lease until response close, but each broker checkout remains bounded to one operation. Do not carry a request-owned client into async tasks or another thread.

For batches outside a request, own the lifetime explicitly and close it on the same thread:

from weft_django import get_client

with get_client() as client:
    first = client.submit_taskspec(first_spec, payload=first_payload)
    second = client.submit_taskspec(second_spec, payload=second_payload)

get_client() always returns a fresh explicitly owned client. It does not expose or borrow the private request client. Forked workers and settings changes discard inherited or stale request entries before admitting new work.

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:

  • tid
  • name
  • status()
  • 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.

For explicit transaction batches, place the client scope outside the atomic block so commit callbacks run while the retained lease is active:

with get_client() as client:
    with transaction.atomic():
        client.submit_taskspec_on_commit(first_spec)
        client.submit_taskspec_on_commit(second_spec)

Reversing those scopes is valid, but callbacks that run after client close use the captured client in bounded one-shot mode. Each helper still registers one independent callback, so Django's callback order and savepoint rollback rules remain unchanged.

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=... and input=... 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 snapshot
  • GET /weft/tasks/<tid>/events/ is the SSE endpoint when TRANSPORT="sse"
  • TRANSPORT="none" disables the SSE endpoint
  • TRANSPORT="channels" switches browser realtime delivery to the optional WebSocket consumer in weft_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.

Metadata

Release files for weft-django 0.9.43

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for weft-django 0.9.43
File Size Uploaded
weft_django-0.9.43.tar.gz 20.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for weft-django 0.9.43
File Interpreter ABI Platform
weft_django-0.9.43-py3-none-any.whl Python 3 none any Details

Total release size: 53.2 kB

Release files / weft_django-0.9.43.tar.gz

Download URL weft_django-0.9.43.tar.gz
Size 20.6 kB
Tags Source
SHA-256 checksum
How to use checksums
0cd544e39f0bf3e3d6910ff418eac4055cea340dcf51644743d949de33078f1a
BLAKE2b-256 checksum
How to use checksums
43a745a056cfe1b823191172b8a0fdbb46674049bd90943857cd39248a86e01a
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.43-py3-none-any.whl

Download URL weft_django-0.9.43-py3-none-any.whl
Size 32.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cf21b7b82fbe207e17d06fb4099daa6dc243bd1d886b577f1d6070bf1e64efcd
BLAKE2b-256 checksum
How to use checksums
1369c5c2fb31e997f2fe36b1300cdb2ec52d5fcfcadd92cdd0be786c0e933756
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 history Release notifications | RSS feed

This release

0.9.43 This release

2 release files

0.9.42

2 release files

0.9.41

2 release files

0.9.40

2 release files

0.9.39

2 release files

0.9.38

2 release files

0.9.37

2 release files

0.9.36

2 release files

0.9.35

2 release files

0.9.33

2 release files

0.9.32

2 release files

0.9.30

2 release files

0.9.6

2 release files

0.9.5

2 release files

0.9.4

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page