Workflow Dynamic Registration
Developer guide for the Enezaframework workflow engine. Part of the Workflow series.
Table of Contents
Overview
Apps can add workflow types at runtime without a project-level workflowconfig/ package (or workflowconfig.py file). The WorkflowTypeRegistry class methods are the public API (enezaworkflowengine/services/workflow_type.py). All three methods first run _load_merged_registry(), so dynamic registrations are added on top of the framework + project merge.
flowchart LR
READY["AppConfig.ready()"] --> REG["WorkflowTypeRegistry.register(name, models)"]
REG --> MERGE["_load_merged_registry()"]
MERGE --> ADD["registry[name] = models"]
ADD --> CACHE["Cached for the process"]register()
Adds or overwrites a workflow entry at runtime — useful in AppConfig.ready() or a signal handler.
from enezaworkflowengine.services.workflow_type import WorkflowTypeRegistry
WorkflowTypeRegistry.register('MyCustomWorkflow', [
{
'slug': 'custom_model',
'label': 'Custom Model',
'app_label': 'myapp',
'model_name': 'CustomModel',
'relationships': [],
},
])
Signature: register(workflow_name: str, models: List[Dict[str, Any]]) (workflow_type.py:211-221). It assigns registry[workflow_name] = models — replacing any existing entry for that name.
register()bypasses the project config import; it writes directly into the merged registry. Because the registry is cached, register early (inAppConfig.ready()) so every later request sees it.
has_workflow()
Checks whether a workflow name is registered (workflow_type.py:205-208).
if WorkflowTypeRegistry.has_workflow('SalesOrderWorkflow'):
models = WorkflowTypeRegistry.get_allowed_models('SalesOrderWorkflow')
Returns True only for names present in the merged registry — the fallback default models are not considered a registered workflow.
get_allowed_models()
Returns the model config list for a workflow, or the default models when the name is unknown or None (workflow_type.py:189-202).
# Specific workflow
models = WorkflowTypeRegistry.get_allowed_models('ExpenseWorkflow')
# Defaults (User, Entity)
models = WorkflowTypeRegistry.get_allowed_models()
models = WorkflowTypeRegistry.get_allowed_models(None)
Registration Patterns
From a Django app
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
def ready(self):
from enezaworkflowengine.services.workflow_type import WorkflowTypeRegistry
WorkflowTypeRegistry.register('MyAppWorkflow', [
{
'slug': 'my_app_model',
'label': 'My App Model',
'app_label': 'myapp',
'model_name': 'MyAppModel',
'relationships': [
{
'field_name': 'related_thing',
'label': 'Related Thing',
'type': 'fk',
'related_app': 'myapp',
'related_model': 'RelatedThing',
},
],
},
])
From a signal handler
Register lazily the first time an app is ready, or from a post_migrate receiver when model availability matters. Always mark the view/choice side too: a registered workflow still needs a Workflownames value if it must be selectable.
| Approach | Use when |
|---|---|
workflowconfig/ (or workflowconfig.py) + MY_PROJECT_WORKFLOWS_CONFIG |
The project owns the workflow definitions. |
register() in AppConfig.ready() |
A reusable app ships its own workflow types. |
register() in a signal handler |
Registration depends on runtime state. |