Workflow Dynamic Registration

Developer guide for the Enezaframework workflow engine. Part of the Workflow series.

Table of Contents

  1. Overview
  2. register()
  3. has_workflow()
  4. get_allowed_models()
  5. Registration Patterns
  6. Related Guides

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.

python
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 (in AppConfig.ready()) so every later request sees it.


has_workflow()

Checks whether a workflow name is registered (workflow_type.py:205-208).

python
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).

python
# 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

python
# 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.