Skip to main content

Custom pages

Added in 1.0.1

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.

ClassAccessGroups
PermissionAdminPagePermissionOption with a view_* codenamemay join navbar / dashboard groups
PersonalAdminPageauthenticated staffcannot join groups
SuperuserAdminPageactive superusermay 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 instance
  • title / sidebar_section_label from AdminSidebarService
  • any extra kwargs you pass

AdminPage attributes

AttributeTypeDefaultPurpose
slugstrrequiredURL segment and sidebar reference
titlelocalized stringrequiredHeading
labellocalized stringrequiredNavigation label
descriptionlocalized string''Optional lead
icon_classFont Awesome class'fa-solid fa-table-list'Sidebar / groups
orderint0Sort key among pages
template_namestr'admin/chassis/page.html'Override per page
filters_fieldset_titlelocalized string_('Фильтры')If the page renders filters
chassis_settingsSettingsSettings()Same collapsed / add flags as ModelAdmin
navbar_groupAdminPageGroupOption or NoneNoneNavbar dropdown
dashboard_groupAdminPageGroupOption or NoneNoneDashboard actions section
route_prefixstr'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:

RouteName
{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:

MethodRole
view(request)list page
get_object(request, object_id) -> T | NoneNone → HTTP 404
get_object_context_data(request, object_id, obj)extra card context
object_actionslist 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.

See also