Stages and Chaining¶
Stages (p.stage())¶
Declare stages as a class attribute:
class MyOp(LiveOperation):
stages = ["Load", "Process", "Save"]
def run(self, p: Progress):
with p.stage("Load"):
data = load_data()
with p.stage("Process"):
result = process(data)
with p.stage("Save"):
save(result)
p.result({"count": len(result)})
Each with p.stage(name): block:
- Sets
op.current_stage(index) andop.stage_states[name] = "active" - Saves to DB (
update_fields=["current_stage", "stage_states"]) - Pushes the updated stage stepper HTML (
#op-stages) - Resets the progress bar to 0%
On exit:
- Success →
stage_states[name] = "done"; stepper updated OperationCancelled→stage_states[name] = "cancelled"; stepper updated- Other exception →
stage_states[name] = "failed"; stepper updated
The stepper is rendered from liveops/_stages.html which iterates
op.stages and looks up state via {{ op.stage_states|get_item:name }}.
Stages must be flat¶
Each call to p.stage(name) looks up name in op.stages by index. The
stages list must be flat and match the exact names passed to p.stage().
A name not in op.stages still runs the block but is not tracked in the
stepper.
Chaining (p.chain_to())¶
Chain two operations without a page reload:
class StepA(LiveOperation):
def run(self, p: Progress):
p.log("Step A done")
step_b = StepB.objects.create(owner=self.owner)
p.chain_to(step_b)
What happens (web mode):
- Current op is finalized (committed to DB as
finished_successfully=True) step_bis enqueued- Via
transaction.on_commit(§19.4): - The DOM container is OOB-swapped to show
step_b's container (hx-swap-oob="outerHTML:#op-<old_pk>") - A
liveop_chainsignal is sent soliveops.jsre-initialises the WebSocket subscription tostep_b's channel (idempotent init closes the old socket cleanly)
The page never reloads. The user sees the transition from step A to step B seamlessly.
Text mode chaining¶
In text/CLI mode, chain_to() runs the next operation inline in the same
thread with the same output stream (no socket handshake).
Text mode stages¶
In text mode, stage boundaries are printed as section headers: