Modal components
AdminModal provides a reusable, permission-aware dialog inside any Chassis
Admin page. The shell is rendered once, while each modal fragment is fetched
only after its trigger is activated.
Define and register a modal
from django.http import HttpRequest, HttpResponse
from django_chassis.components import PermissionAdminModal
from django_chassis.options import PermissionOption
class ReconcileModal(PermissionAdminModal):
slug = 'reconcile'
title = 'Reconcile records'
template_name = 'admin/project/reconcile_modal.html'
permission = PermissionOption(
app_label='operations',
model='reconciliation',
codename='view_reconciliation',
name='Can view reconciliation'
)
def view(self, request: HttpRequest) -> HttpResponse:
return self.render(request=request, form=ReconcileForm())
class CustomAdminSite(ChassisAdminSiteMixin, AdminSite):
chassis_modal_classes = (ReconcileModal,)
PermissionAdminModal registers its PermissionOption and checks it both
when Chassis builds the trigger context and when the fragment endpoint handles
GET or POST. Use AdminModal directly when visibility needs a custom
has_view_permission() implementation.
Render a trigger
Load chassis_admin in any Admin template:
{% load chassis_admin %}
{% chassis_modal_trigger "reconcile" "Open reconciliation" "fa-solid fa-scale-balanced" %}
The optional fourth argument sets the trigger CSS class. A trigger is omitted when its modal is unavailable to the current user.
Navbar dropdown children can open the same dialog by returning an
AdminNavbarChildItemDTO with opens_modal=True and the modal URL.
Fragment template and forms
Extend the shared fragment and mark forms for asynchronous submission:
{% extends "admin/chassis/modal.html" %}
{% block chassis_modal_body %}
<p class="chassis-modal-description">Review the data before continuing.</p>
{% include "admin/chassis/modal_form_errors.html" %}
<form method="post" action="{{ chassis_modal.get_url }}" data-chassis-modal-form>
{% csrf_token %}
{{ form.as_p }}
<div class="chassis-modal-actions">
<button type="button" data-chassis-modal-close>Cancel</button>
<button type="submit">Continue</button>
</div>
</form>
{% endblock %}
Use AdminModal.redirect(url) after a successful POST. Chassis follows the
redirect without an intermediate page request, preserving Django messages.
close_on_backdrop and close_on_escape default to True and can be
configured independently. The modal shell and scrollable table helpers are
bounded by the browser viewport.
Security contract
Modal endpoints are wrapped with AdminSite.admin_view(), require the
X-Chassis-Modal: 1 request header, and re-run has_view_permission() on
every request. Calling an endpoint directly therefore returns HTTP 400 rather
than rendering a standalone Admin page.