Core - Admin Infrastructure
Location: api/nextango/apps/core/admin/
Last Updated: 2026-07-09
Overview
Core ships the shared Django admin infrastructure used by the domain app admins: editorial form fields that render Sanity reference JSON as labeled dropdowns and checklists, a form mixin for editing nested Sanity objects, read-only JSON display helpers, sync-health list indicators, and the deletion guard that enforces the platform's deletion-semantics standard. Everything exports from nextango.apps.core.admin (admin/__init__.py). The users, products, stores, promotions, and procurement admins build on these pieces.
These are internal admin (staff-facing) tools, not API surface. They mount on existing JSONField storage through form-field overrides; adopting them requires no data-model change or migration.
Editorial Widgets (admin/sanity_widgets.py)
Form fields that let an editor work with Sanity-shaped JSON without ever seeing a raw _ref, _key, or _type. The Sanity schema field type is the spec; each widget maps one schema shape to one stored JSON shape:
| Sanity schema type | Field | Stored shape |
|---|---|---|
reference(T) | SanityRefField | {"_type": "reference", "_ref": id} or a bare id string |
array of reference(T) | SanityRefArrayField | [{"_type": "reference", "_ref": id, "_key": key}] or a bare-id list |
string + options.list | SanityOptionsField | the chosen option value (plain string) |
array of string + options.list | SanityStringChecklistField | ["v1", "v2"] |
array of string, layout: 'tags' (freeform) | SanityTagListField | ["tag1", "tag2"] |
SanityRefField
A ChoiceField rendered as a labeled dropdown. Constructed with the target Django model; choices are every row carrying a sanity_id (a reference needs a Sanity id to point at), labeled by label_from (default name). value_kind='object' (default) cleans to a {'_type': 'reference', '_ref': ...} object; value_kind='id' cleans to a bare id string. The empty choice clears the value to None. An optional queryset pre-filters the choices.
SanityRefArrayField
A MultipleChoiceField rendered as a labeled multiselect, for reference arrays. value_kind='object' cleans to reference objects with a deterministic _key derived from the _ref (uuid5), so a given reference carries the same key across saves and round-trips are stable. value_kind='id' cleans to a bare-id list, matching fields stored as bare ids such as Store.manager_ids. Tolerates stored items shaped as {'_ref': id} objects, nested legacy {'store': {'_ref': id}} objects, or bare strings.
SanityOptionsField and SanityStringChecklistField
Dropdown and checkbox-list fields for Sanity options.list strings. Both accept the schema options as bare strings or (value, label) pairs. The checklist cleans an empty selection to []; the options field cleans empty to None.
SanityTagListField
Text input for freeform tag arrays (no options.list). The editor types comma- or newline-separated tags; the field cleans to a list of trimmed strings.
SanityObjectFormMixin (admin/sanity_object_form.py)
A ModelForm mixin for editing a single nested Sanity object stored in a JSONField (for example Product.organization) as a group of prefixed sub-fields named <jsonfield>__<subfield>, built from the widget leaves above.
The concrete form lists its managed JSONFields in SANITY_OBJECT_FIELDS and declares the sub-fields. On form init, each sub-field's initial value is seeded from the instance JSON, and the raw JSONField is removed from the form (the mixin owns writing it). On save, the JSON object is rebuilt from its original value: declared edits are overlaid, cleared sub-fields are dropped, and keys with no declared sub-field pass through untouched, so unmodeled data is never lost. Dates and datetimes are coerced to ISO strings.
One invariant matters when adding conditional visibility: hide rows, do not disable controls. A hidden sub-field still submits its stored value and round-trips untouched; a disabled or omitted control submits nothing and reads as "cleared".
class SupplierAdminForm(SanityObjectFormMixin, forms.ModelForm):
SANITY_OBJECT_FIELDS = ('contact_info',)
contact_info__email = forms.EmailField(required=False)
contact_info__phone = forms.CharField(required=False)
Display Helpers (admin/display.py)
Read-only, XSS-safe rendering for JSON and reference data in admin change pages. All rendering goes through format_html/format_html_join so content is HTML-escaped. These render read-only views by design; sync-mirror JSON fields must not be made editable as raw JSON through the admin.
pretty_json(value, summary='Raw JSON', collapsed=True)renders a JSON value as a collapsible, pretty-printed block. Empty values render as a muted placeholder.reference_list(value, noun='items', collapsed=True)renders a reference array as a counted, collapsible list, accepting ref strings or Sanity ref dicts.json_readonly_field(field_name, label, kind='json', noun='items', summary='Raw JSON')is a factory producing a read-only display method over a JSON or reference field, replacing per-model boilerplate:
class EmployeeAdmin(admin.ModelAdmin):
readonly_fields = ['store_assignments_display']
store_assignments_display = json_readonly_field(
'store_assignments', 'Store Assignments', kind='refs', noun='stores'
)
Sync Health Admin (admin/sync_admin_mixins.py)
Admin support for models inheriting SanityIntegratedModel (see Models).
SyncHealthAdminMixinadds list-display methods:sync_lag(revision lag number),sync_health_indicator(color-coded: green at lag 0, orange at 1-5, red at 6-10, dark red above 10),needs_sync_display(boolean),sync_status_display, andlast_synced_display(relative time, color-coded by age).SyncHealthFilteris a list filter with the same four lag categories.SyncRevisionFieldset.get_fieldset(collapsed=True)returns a pre-configured fieldset grouping the sync and revision fields.SYNC_READONLY_FIELDSlists the sync bookkeeping fields to mark read-only in detail views.
@admin.register(MyModel)
class MyModelAdmin(SyncHealthAdminMixin, admin.ModelAdmin):
list_display = ['id', 'name', 'sync_lag', 'sync_health_indicator']
list_filter = ['sync_status', SyncHealthFilter]
readonly_fields = SYNC_READONLY_FIELDS
SanityDeletionGuardAdminMixin (admin/deletion_guard_mixins.py)
Enforces the deletion-semantics contract described in Models at the admin layer. Place it before admin.ModelAdmin in the MRO.
- Sanity-owned models (
_is_sanity_sot=True):has_delete_permissionreturnsFalseand thedelete_selectedbulk action is removed. Deletion must originate from Sanity. - Django-owned models (
_is_sanity_sot=False): the delete view and bulk action route every row throughsafe_delete(origin='django_admin', hard_delete=True). Results surface through the Django messages framework, including dependency blockers when a delete is refused.
The mixin fails closed: a missing classifier defaults to blocking, non-SanityIntegratedModel adopters are blocked, and any exception in the gate logic preserves the row and surfaces an error message.