Workflow Query View Integration
Developer guide for the Enezaframework workflow engine. Part of the Workflow series.
Table of Contents
- Overview
- SaveQueryView (POST)
- SaveQueryDetailView (GET execute=1)
- BuildQueryView (Template Context)
- Related Guides
Overview
The query builder views are the HTTP surface of the enhancements. SaveQueryView accepts annotations when saving, SaveQueryDetailView accepts object_id when executing, and BuildQueryView exposes aggregate metadata to the builder UI.
flowchart LR
UI["query builder UI"] -->|POST JSON| SQV["SaveQueryView"]
SQV --> SQ["SavedQuery.filters_json"]
UI -->|GET execute| SQD["SaveQueryDetailView"]
SQD -->|object_id| SQ
BQV["BuildQueryView"] -->|filters_by_model_json| UIAll views live in enezaworkflowengine/views/querybuilder.py.
SaveQueryView (POST)
SaveQueryView.post() (views/querybuilder.py:79-128) accepts annotations in either of two shapes:
# Inside the query object (preferred):
{
"query": {
"condition": "AND",
"annotations": [...],
"rules": [...]
}
}
# As a separate top-level key (also supported):
{
"query": {"condition": "AND", "rules": [...]},
"annotations": [...]
}
The view merges a separate annotations key into filters_json before saving (views/querybuilder.py:104-107):
filters_json = rules
annotations = data.get('annotations')
if annotations and isinstance(filters_json, dict):
filters_json['annotations'] = annotations
It also requires name, model_slug, and query, and resolves the model by slug against get_allowed_models() (views/querybuilder.py:90-103).
SaveQueryDetailView (GET execute=1)
SaveQueryDetailView.get_context_data() executes the query when execute=1 and forwards an optional object_id (views/querybuilder.py:230-248):
if self.request.GET.get('execute') == '1':
object_id = self.request.GET.get('object_id')
queryset = self.object.execute(object_id=object_id)
total_count = queryset.count()
if total_count > 0:
results = list(queryset.values()[:100])
Request shape:
GET /workflow/savedquery/<pk>/?execute=1&object_id=<uuid>
If execution raises, the error is captured into context['execution_error'] and the count is set to 0 (views/querybuilder.py:246-248).
BuildQueryView (Template Context)
BuildQueryView.get_context_data() builds filters_by_model and attaches an aggregates structure per model (views/querybuilder.py:68-74):
filters_by_model = {
m['slug']: self.get_model_filters(m['app_label'], m['model_name'], model_config=m)
for m in allowed_models
}
for m in allowed_models:
filters_by_model[m['slug']]['aggregates'] = self.get_aggregate_options(m)
ctx['filters_by_model_json'] = json.dumps(filters_by_model)
get_aggregate_options() returns a hierarchical structure — not a flat option list (views/mixin.py:152-214):
{
"frameworktestdocument": {
"direct": [/* field filters */],
"related": [/* related model filters */],
"aggregates": {
"related_models": [
{
"field_name": "lines",
"label": "Test Document Line",
"fields": [
{"name": "amount", "label": "Amount", "type": "double"},
{"name": "quantity", "label": "Quantity", "type": "integer"}
]
}
],
"functions": [
{"name": "sum", "label": "Sum"},
{"name": "count", "label": "Count"},
{"name": "avg", "label": "Average"},
{"name": "min", "label": "Minimum"},
{"name": "max", "label": "Maximum"}
]
}
}
}
The builder UI combines a related model, a numeric field, and a function to form an annotation, then derives the alias. See Workflow Aggregate Metadata API.