Custom pages
Custom pages live on the AdminSite. They reuse the Chassis shell
(sidebar, navbar, brand, breadcrumbs) but are not ModelAdmins. Each class
must declare exactly one access contract.
| Class | Access | Groups |
|---|---|---|
PermissionAdminPage | PermissionOption with a view_* codename | may join navbar / dashboard groups |
PersonalAdminPage | authenticated staff | cannot join groups |
SuperuserAdminPage | active superuser | may join groups |
All of them subclass AdminPage. Site construction raises
ImproperlyConfigured if a class mixes two contracts, omits all three, or
puts a personal page in a group.
from django.http import HttpRequest, HttpResponse
from django.utils.translation import gettext_lazy as _
from django_chassis.options import AdminPageGroupOption, PermissionOption
from django_chassis.views import PermissionAdminPage
reports_group = AdminPageGroupOption(slug='reports', label=_('Reports'), icon_class='fa-solid fa-chart-line', order=10)
view_inventory = PermissionOption(
app_label='catalog', model='book', codename='view_inventory', name=_('Can view inventory')
)
class InventoryPage(PermissionAdminPage):
slug = 'inventory'
title = _('Inventory')
label = _('Inventory')
description = _('Stock overview')
icon_class = 'fa-solid fa-boxes-stacked'
permission = view_inventory
navbar_group = reports_group
dashboard_group = reports_group
def view(self, request: HttpRequest) -> HttpResponse:
return self.render(request, rows=[])
Register the class on the site:
class CustomAdminSite(ChassisAdminSiteMixin, AdminSite):
chassis_page_classes = (InventoryPage,)
AdminPageService mounts the routes. Put a PermissionAdminPage in
sidebar_items with SidebarPageItemOption(page_slug='inventory') if it
should appear in the sidebar. Personal and superuser pages cannot be
sidebar children.
Page anatomy
Default template: admin/chassis/page.html.
┌─ breadcrumbs ─────────────────────────────────────────────┐
│ Dashboard › Sidebar section › page label │
├─ toolbar (`page_actions`) ────────────────────────────────┤
│ optional page-level buttons (information pages) │
├─ title / description ─────────────────────────────────────┤
├─ `chassis_page_content` ──────────────────────────────────┤
│ whatever `view()` / `render()` puts in context │
└───────────────────────────────────────────────────────────┘
dispatch() checks has_view_permission. Failure raises
PermissionDenied (Chassis error page if
AdminErrorPageMiddleware is installed). Success calls view().
render(request, **context) merges:
admin_site.each_context(request)(brand, sidebar, navbar, footer)chassis_page— the page instancetitle/sidebar_section_labelfromAdminSidebarService- any extra kwargs you pass
AdminPage attributes
| Attribute | Type | Default | Purpose |
|---|---|---|---|
slug | str | required | URL segment and sidebar reference |
title | localized string | required | Heading |
label | localized string | required | Navigation label |
description | localized string | '' | Optional lead |
icon_class | Font Awesome class | 'fa-solid fa-table-list' | Sidebar / groups |
order | int | 0 | Sort key among pages |
template_name | str | 'admin/chassis/page.html' | Override per page |
filters_fieldset_title | localized string | _('Фильтры') | If the page renders filters |
chassis_settings | Settings | Settings() | Same collapsed / add flags as ModelAdmin |
navbar_group | AdminPageGroupOption or None | None | Navbar dropdown |
dashboard_group | AdminPageGroupOption or None | None | Dashboard actions section |
route_prefix | str | 'pages' | First URL segment |
URL: {admin}/{route_prefix}/{slug}/. URL name: chassis_page_{slug}
(namespaced with the AdminSite name).
Implement view(self, request) (not get() / post() on the base —
dispatch always calls view). Branch on request.method inside
view if you need POST.
Access contracts
PermissionAdminPage
permission must be a PermissionOption whose codename starts with
view_. The site registers it automatically. The check uses
AdminPermissionService / has_request_user_permission (group-only by
default).
PersonalAdminPage
Any authenticated staff user. Must not set navbar_group or
dashboard_group. Use this for "my profile", "my tokens", and similar
per-user screens.
SuperuserAdminPage
request.user.is_active and is_superuser.
Object pages
AdminObjectPage[T] extends a custom page with a list URL, an
object card, and @object_action methods. The object does not
have to be a Django model: you resolve it yourself.
Extra routes:
| Route | Name |
|---|---|
{prefix}/{slug}/ | chassis_page_{slug} — list (view) |
{prefix}/{slug}/{object_id}/ | chassis_page_{slug}_object |
{prefix}/{slug}/{object_id}/actions/{action_name}/ | chassis_page_{slug}_object_action |
You implement:
| Method | Role |
|---|---|
view(request) | list page |
get_object(request, object_id) -> T | None | None → HTTP 404 |
get_object_context_data(request, object_id, obj) | extra card context |
object_actions | list of @object_action method names |
Card template: admin/chassis/object_page.html (object_template_name).
The card toolbar lists allowed object actions. Permission and
condition / condition_method are re-checked on the action URL.
If the object disappeared, the action redirects to the list with an error message. Unknown or forbidden actions redirect back to the card.
Information pages
AdminInformationPage is a structured report (sections, alerts, header
actions, timelines). See Information pages.