Glue Helpers
Purpose: Provide a consistent, retry-aware interface for all client-side Django Glue operations — whether calling a custom JSON endpoint, loading a single model object, or querying a full queryset.
Warning
This guide assumes a working knowledge of Django Glue. The helpers wrap the core Django Glue primitives (django_glue_fetch, ModelObjectGlue, QuerySetGlue) and are not a replacement for them.
Why Glue Helpers?
Raw Django Glue calls are powerful but verbose. Every call needs its own error handling, retry logic, and response dispatch. Glue Helpers standardise that boilerplate into three focused classes:
| Helper | When to Use |
|---|---|
GlueFetchHelper |
Calling a custom JSON endpoint (not a Glue model/queryset) |
GlueObjectHelper |
Loading or interacting with a single ModelObjectGlue instance |
GlueQuerySetHelper |
Querying, filtering, updating, or deleting via a QuerySetGlue |
All three extend GlueRetryHelper, giving every call built-in retry-on-failure behaviour and automatic error dispatching.
Quick Start
Load a model object on page init and call a custom action endpoint on user interaction.
Backend:
from django.http import JsonResponse
from django_spire.contrib.responses.json_response import success_json_response, error_json_response
from django_spire.core.shortcuts import get_object_or_null_obj
from django_spire.core.decorators import valid_ajax_request_required
from myapp.models import Treasure
@valid_ajax_request_required
def bury_treasure_view(request, pk: int) -> JsonResponse:
treasure = get_object_or_null_obj(Treasure, pk=pk)
if treasure.id is None:
return error_json_response('Treasure not found.')
treasure.is_buried = True
treasure.services.save_model_obj()
return success_json_response(f'{treasure.name} has been buried!')
Frontend:
{
treasure: new ModelObjectGlue('{ unique_name }'),
async init() {
await GlueObjectHelper.tryGlueGet(this.treasure, 3);
},
async buryTreasure() {
const url = `{ url "treasure:bury" pk=0 }`.replace('0', this.treasure.id);
await GlueFetchHelper.tryGlueFetch(url, {
successHandler: (response) => {
console.log(response.message);
},
});
},
}
Core Concepts
GlueFetchHelper
Use when calling any custom JSON endpoint — one that does not map directly to a QuerySetGlue operation.
| Parameter | Type | Default | Description |
|---|---|---|---|
url |
string |
required | The endpoint URL |
options.payload |
Object |
{} |
Request body data |
options.successHandler |
Function |
— | Called with the response on success |
options.errorHandler |
Function |
— | Called with the response on error |
options.method |
string |
'POST' |
HTTP method |
options.maxRetries |
number |
1 |
Maximum retry attempts |
options.delayMs |
number |
100 |
Milliseconds between retries |
Returns { success: boolean, response?: Object, error?: Error }.
GlueObjectHelper
Use to load data for a single ModelObjectGlue instance — typically during init() of an Alpine.js component.
| Parameter | Type | Default | Description |
|---|---|---|---|
glueObject |
ModelObjectGlue |
required | The Glue object to fetch |
maxRetries |
number |
1 |
Maximum retry attempts |
errorHelper |
Function |
null |
Called after each failed attempt |
delayMs |
number |
100 |
Milliseconds between retries |
GlueQuerySetHelper
Use for any queryset operation — fetching all records, filtering, getting one by id, updating, or deleting.
| Method | Description |
|---|---|
tryAll(glueQuerySet, ...) |
Fetch all objects in the queryset |
tryFilter(glueQuerySet, filterParams, ...) |
Filter objects by parameters |
tryGet(glueQuerySet, id, ...) |
Fetch a single object by id |
tryDelete(glueQuerySet, id, ...) |
Delete an object by id |
tryUpdate(glueQuerySet, queryModelObject, field, ...) |
Update one or all fields on an object |
tryMethod(glueQuerySet, id, method, kwargs, ...) |
Call a named method on an object |
tryNullObject(glueQuerySet, ...) |
Fetch the null/empty object from the queryset |
tryToChoices(glueQuerySet, filterParams, ...) |
Convert queryset objects to [value, label] choice pairs |
All methods share the same optional tail arguments: maxRetries, errorHelper, delayMs.
Retry Logic
All helpers extend GlueRetryHelper, which retries failed calls up to maxRetries times with a delayMs pause between each attempt. If all retries are exhausted, a generic error event is dispatched automatically — no extra error handling is required in your component.
// Retry up to 3 times with a 300ms delay
await GlueQuerySetHelper.tryAll(this.treasure_queryset, 3, null, 300);
// Run a callback between retries
await GlueFetchHelper.tryGlueFetch(url, {
maxRetries: 3,
delayMs: 200,
errorHelper: () => { this.isRetrying = true; },
successHandler: () => { this.isRetrying = false; },
});
Main Operations
Calling a Custom Endpoint
Call an endpoint and update local state with the response:
async discoverLocation(locationId) {
const url = `{ url "treasure:discover" pk=0 }`.replace('0', locationId);
await GlueFetchHelper.tryGlueFetch(url, {
successHandler: (response) => {
if (response.locations) {
this.locations = response.locations;
}
},
errorHandler: () => {
console.error('Could not mark location as discovered.');
},
maxRetries: 2,
delayMs: 200,
});
}
Loading a Model Object on Init
{
treasure: new ModelObjectGlue('{ unique_name }'),
async init() {
await GlueObjectHelper.tryGlueGet(this.treasure, 3);
},
}
Populating a Select from a QuerySet
{
crew_queryset: new QuerySetGlue('crew_members'),
assigned_crew: new GlueCharField('assigned_crew'),
async init() {
this.assigned_crew.choices = await GlueQuerySetHelper.tryToChoices(this.crew_queryset);
},
}
Deleting a Record
{
treasures: { treasures|safe },
treasure_queryset: new QuerySetGlue('treasures'),
async deleteTreasure(id) {
await GlueQuerySetHelper.tryDelete(this.treasure_queryset, id, 2);
this.treasures = this.treasures.filter(t => t.id !== id);
},
}