Actions and dialogs
Operations that are not create, read, update or delete: publishing a product, refunding an order, closing an accounting period.
An action is two declarations on the backend: the endpoint that does the work, and the entry in the resource schema that turns it into a button. Both live in the backend; the frontend needs no change either way. Whether the button asks for confirmation, opens a form, or runs in the background is part of the schema entry.
Three shapes of action
| Shape | The user sees | Declare it with |
|---|---|---|
| Immediate | A button that acts at once | An action with no form and no dialog |
| Confirmed | A confirmation dialog first | A confirm dialog on the action |
| Form-driven | A form collecting parameters | An action view with its own fields |
An immediate action
- Python
- Ruby
from rest_framework.decorators import action
from habarico import ModelAdmin
from habarico.response import ActionResponse
from habarico.schema.generators import RequestMethodType, ResourceActionSchemaGenerator
from habarico.views import AdminModelViewSet
class ProductViewSet(AdminModelViewSet):
@action(detail=True, methods=["post"], url_path="publish")
def publish(self, request, pk=None):
product = self.get_object()
product.status = "published"
product.save(update_fields=["status"])
return ActionResponse(message="Product published.")
class ProductAdmin(ModelAdmin):
model = Product
base_viewset = ProductViewSet
action_schema_generators = [
ResourceActionSchemaGenerator(
name="publish",
endpoint_action_name="publish",
is_detail=True,
method=RequestMethodType.POST,
label="Publish",
),
]
ActionResponse builds the meta envelope the frontend reads; the @action alone is only the
endpoint, and without the generator no button is rendered.
A confirmed action
Confirmation is a property of the action, not of the button:
- Python
- Ruby
ResourceActionSchemaGenerator(
name="close_accounting_period",
endpoint_action_name="close_accounting_period",
is_detail=True,
method=RequestMethodType.PUT,
label="Close period",
confirm_dialog_title="Close the accounting period?",
confirm_dialog_message="Orders in this period can no longer be edited.",
)
The confirmation keys belong to the action, so they exist on ResourceAction and ViewAction —
not on the plain Habarico::ActionReference a menu entry uses. A Ruby definition object ignores a
key it does not declare without a word, so a confirmation put on the wrong class simply never
appears.
The confirmation texts reach the button through the schema, so a custom button built with the ButtonProvider must pass them on — dropping them silently removes a confirmation the backend asked for.
A form-driven action
An action that needs input gets its own view: the schema for
…/{id}/{action}/schema/ describes the fields, and
SchemaAction renders them as a form.
Because the schema is fetched per record, the same action can ask for different fields on different records — and an action a user may not run simply is not in their schema.
Long-running actions
A response whose meta.state is running turns the action into a background task: the frontend
polls the same endpoint every five seconds and notifies on each state change.
{"meta": {"state": "running", "message": "Generating invoices…"}}
When the final response arrives with state: "success", the record and its schema are refreshed —
so an action that changes what a record allows updates its own buttons.
Set isBackground when you call the action from your own button
(useCallAction): on mount it asks for the current state, which is
what lets a task survive a page reload.
Chaining actions
A response may carry meta.triggerAction, another action schema. The frontend runs it as soon as
the first one finishes — how a "refund" action can lead straight into a "print credit note" form
without the user going looking for it.
See also
- Custom actions — the Ruby DSL in full
- Action view schema generator — the Python side
- SchemaButtons — where the buttons appear
- useCallAction — calling an action from your own UI