Conditions & Done Methods
Two optional method types give you control over every transition: condition methods gate whether a transition can run, and done methods execute logic after one completes.
Condition Methods
A condition method is called before a transition executes. If it returns False, the transition is blocked and the button is hidden (or an error is shown).
Naming: <transition_name>_condition
def submit_condition(self, request, object_instance, **kwargs):
"""Check if the record is ready to submit."""
if not object_instance.name:
return False, "Name is required before submitting"
if not object_instance.email:
return False, "Email is required before submitting"
return True, "Ready to submit"
Parameters
| Parameter | Description |
|---|---|
request | The Django HttpRequest object |
object_instance | The model record being transitioned |
**kwargs | Additional keyword arguments |
Return Value
Return a tuple (bool, str):
True, "message"— transition is allowedFalse, "reason"— transition is blocked; reason shown to user
Done Methods
A done method runs after a transition completes successfully. Use it to update metadata, send notifications, trigger async tasks, or any post-transition logic.
Naming: <transition_name>_done
def approve_done(self, request, object_instance, transaction_obj):
"""Runs after the 'approve' transition completes."""
from django.utils import timezone
# Update metadata
object_instance.approved_at = timezone.now()
object_instance.approved_by = request.user
object_instance.save() # don't forget to save!
# Send notification
send_approval_notification(object_instance)
Parameters
| Parameter | Description |
|---|---|
request | The Django HttpRequest object |
object_instance | The model record that was transitioned |
transaction_obj | The WorkflowTransaction record created for this transition |
:::tip Always call .save()
If you modify object_instance in a done method, always call object_instance.save() — it is not auto-saved.
:::
Full Example
class OrderWorkflow(WorkflowBase):
status_transitions = [
{
"name": "submit",
"display_name": "Submit",
"from": "draft",
"to": "pending",
"confirmation_message": "Submit this order for approval?",
},
{
"name": "approve",
"display_name": "Approve",
"from": "pending",
"to": "active",
"roles": ["Manager"],
"form": ApprovalForm,
},
{
"name": "reject",
"display_name": "Reject",
"from": "pending",
"to": "rejected",
"roles": ["Manager"],
},
]
def submit_condition(self, request, object_instance, **kwargs):
if not object_instance.line_items.exists():
return False, "Order must have at least one line item"
return True, "Ready to submit"
def approve_done(self, request, object_instance, transaction_obj):
from django.utils import timezone
object_instance.approved_at = timezone.now()
object_instance.approved_by = request.user
object_instance.save()
notify_order_approved(object_instance)
def reject_done(self, request, object_instance, transaction_obj):
from django.utils import timezone
object_instance.rejected_at = timezone.now()
object_instance.save()
notify_order_rejected(object_instance)
class Meta:
model = Order
on_create_status = "draft"
statuses = {
"draft": {"color": "#717680", "label": "Draft"},
"pending": {"color": "#F59E0B", "label": "Pending"},
"active": {"color": "#12B76A", "label": "Active"},
"rejected":{"color": "#F04438", "label": "Rejected"},
}