Cabinet Internal Modules
This section contains the lower-level cabinet registry, dashboard providers, Redis managers, view helpers, and model support classes.
Registry
codex_django.cabinet.registry
In-memory registry for cabinet navigation and dashboard contributions.
Feature apps interact with this module through :func:declare.
The registry stores sections, widgets, actions, sidebar items, and shortcuts
for rendering via context processors and views.
Supports two APIs:
- Legacy (v1): declare(module, section=CabinetSection(...))
- New (v2): declare(module, space="staff", topbar=TopbarEntry(...), sidebar=[...])
Classes
CabinetRegistry
Store cabinet contributions in process memory.
Source code in src/codex_django/cabinet/registry.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | |
Attributes
sections
property
Return all registered sections ordered by their display order.
dashboard_widgets
property
Return all registered dashboard widgets ordered by their display order.
topbar_actions
property
Return topbar action declarations in registration order.
global_actions
property
Return global action declarations in registration order.
Functions
register_branding(space, label=None, icon=None)
Set branding metadata for a specific space.
Source code in src/codex_django/cabinet/registry.py
54 55 56 57 58 59 60 61 62 63 64 | |
get_branding(space)
Return branding metadata for a specific space.
Source code in src/codex_django/cabinet/registry.py
66 67 68 | |
register_v2(module, space, topbar=None, sidebar=None, shortcuts=None, dashboard_widgets=None, settings_url=None)
Register cabinet contributions for the new two-space model.
Source code in src/codex_django/cabinet/registry.py
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | |
get_topbar_entries(group=None)
Return topbar entries, optionally filtered by group.
Source code in src/codex_django/cabinet/registry.py
110 111 112 113 114 115 116 117 | |
get_sidebar(space, module)
Return sidebar items for a given space and module.
Source code in src/codex_django/cabinet/registry.py
119 120 121 | |
get_shortcuts(space, module)
Return topbar shortcuts for a given space and module.
Source code in src/codex_django/cabinet/registry.py
123 124 125 | |
get_settings_url(space, module)
Return the custom settings URL for a given space and module.
Source code in src/codex_django/cabinet/registry.py
127 128 129 | |
get_module_topbar(module)
Return the TopbarEntry associated with a specific module.
Source code in src/codex_django/cabinet/registry.py
131 132 133 | |
iter_modules(space=None)
Return registered module names without exposing private storage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
space
|
str | None
|
Optional cabinet space filter. When omitted, modules from all spaces are returned. |
None
|
Source code in src/codex_django/cabinet/registry.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
iter_sidebar(space=None)
Return sidebar registrations as (space, module, items) tuples.
Source code in src/codex_django/cabinet/registry.py
154 155 156 157 158 159 160 161 | |
get_default_module(space, group=None)
Return the first registered module for a cabinet space.
The selection is deterministic and prefers topbar order when a module
has a topbar declaration. None is returned when the registry has no
module for the requested space.
Source code in src/codex_django/cabinet/registry.py
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | |
get_module_config(space, module)
Return a read-only snapshot for space/module.
Source code in src/codex_django/cabinet/registry.py
181 182 183 184 185 186 187 188 189 190 | |
register(module_name, section=None, dashboard_widget=None, topbar_actions=None, actions=None)
Register cabinet contributions (legacy API).
.. deprecated:: Use :meth:register_v2 with the two-space model.
Source code in src/codex_django/cabinet/registry.py
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | |
get_sections(nav_group=None)
Return registered sections, optionally filtered by nav group.
Source code in src/codex_django/cabinet/registry.py
228 229 230 231 232 233 | |
get_dashboard_widgets(nav_group=None)
Return registered dashboard widgets, optionally filtered by nav group.
Source code in src/codex_django/cabinet/registry.py
240 241 242 243 244 245 | |
Functions
declare(module, space=None, topbar=None, sidebar=None, shortcuts=None, settings_url=None, dashboard_widgets=None, section=None, dashboard_widget=None, **kwargs)
Public API for cabinet.py in feature apps. Analogous to admin.site.register().
New two-space API (when space is provided)::
declare(
module="booking",
space="staff",
topbar=TopbarEntry(group="services", label="Booking", ...),
sidebar=[SidebarItem(label="Schedule", url="booking:schedule", ...)],
shortcuts=[Shortcut(label="New", url="booking:new", icon="bi-plus")],
)
Legacy API (when space is None)::
declare(module="booking", section=CabinetSection(...))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
str
|
Django app name or feature identifier. |
required |
space
|
str | None
|
|
None
|
topbar
|
TopbarEntry | None
|
Topbar dropdown entry (v2 only). |
None
|
sidebar
|
list[SidebarItem] | None
|
Sidebar navigation items (v2 only). |
None
|
shortcuts
|
list[Shortcut] | None
|
Topbar shortcuts for this module (v2 only). |
None
|
section
|
CabinetSection | None
|
Legacy :class: |
None
|
dashboard_widgets
|
list[DashboardWidget] | DashboardWidget | str | None
|
Dashboard widget declaration (v2). |
None
|
dashboard_widget
|
DashboardWidget | str | None
|
Legacy dashboard widget declaration (v1). |
None
|
**kwargs
|
Any
|
Additional keyword arguments forwarded to registry. |
{}
|
Source code in src/codex_django/cabinet/registry.py
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | |
configure_space(space, label=None, icon=None)
Configure space-wide metadata (branding, icon, etc).
Example::
configure_space(
space="staff",
label=_("Admin Panel"),
icon="bi-shield-lock",
)
Source code in src/codex_django/cabinet/registry.py
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | |
Runtime resolver
codex_django.cabinet.runtime
Runtime context resolution for cabinet shells.
The resolver is intentionally project-agnostic. It converts a Django request plus registry declarations into a stable cabinet context without hardcoding a single project URL shape.
Classes
CabinetSpaceConfig
dataclass
URL and fallback policy for one cabinet space.
Source code in src/codex_django/cabinet/runtime.py
22 23 24 25 26 27 28 29 30 | |
CabinetRequestContext
dataclass
Resolved cabinet shell data for a request.
Source code in src/codex_django/cabinet/runtime.py
33 34 35 36 37 38 39 40 41 42 43 44 45 | |
CabinetRuntimeResolver
Resolve cabinet space/module metadata from a request and registry.
Source code in src/codex_django/cabinet/runtime.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | |
Functions
get_space_configs()
Return space configuration from Django settings with safe defaults.
Source code in src/codex_django/cabinet/runtime.py
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | |
View mixins
codex_django.cabinet.mixins
Reusable class-based view helpers for cabinet modules.
Classes
CabinetModuleMixin
Bases: View
Attach cabinet metadata to the request before rendering a view.
Source code in src/codex_django/cabinet/mixins.py
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
Functions
dispatch(request, *args, **kwargs)
Store cabinet routing metadata on the request before dispatch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
Any
|
Django request object being dispatched. |
required |
*args
|
Any
|
Positional arguments forwarded to the parent view. |
()
|
**kwargs
|
Any
|
Keyword arguments forwarded to the parent view. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
The response returned by the parent |
Source code in src/codex_django/cabinet/mixins.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
StaffRequiredMixin
Bases: LoginRequiredMixin, UserPassesTestMixin
Require an authenticated staff or superuser account.
Source code in src/codex_django/cabinet/mixins.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | |
Functions
test_func()
Return whether the current request user can access staff cabinet views.
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
superuser. Otherwise returns |
Source code in src/codex_django/cabinet/mixins.py
44 45 46 47 48 49 50 51 52 53 54 55 56 | |
OwnerRequiredMixin
Bases: StaffRequiredMixin
Require an authenticated owner/superuser account.
Source code in src/codex_django/cabinet/mixins.py
59 60 61 62 63 64 65 66 67 68 69 70 71 | |
Functions
test_func()
Return whether the current request user can access owner-only views.
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
Otherwise returns |
Source code in src/codex_django/cabinet/mixins.py
62 63 64 65 66 67 68 69 70 71 | |
CabinetTemplateView
Bases: CabinetModuleMixin, TemplateView
TemplateView variant that publishes cabinet metadata during dispatch.
Source code in src/codex_django/cabinet/mixins.py
74 75 | |
Modal presenters
codex_django.cabinet.presenters.modal
Generic modal-state presenter for cabinet components.
Classes
ModalPresenter
Map generic domain state into ModalContentData.
Source code in src/codex_django/cabinet/presenters/modal.py
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
Functions
__init__(action_url_resolver=None)
Initialize the presenter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
action_url_resolver
|
ActionUrlResolver | None
|
Optional callable used to derive action URLs
from raw action objects before building |
None
|
Source code in src/codex_django/cabinet/presenters/modal.py
26 27 28 29 30 31 32 33 | |
present(state)
Convert a generic state object into typed modal content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
Any
|
Mapping-like or attribute-based object describing modal title, profile, summary items, form fields, and actions. |
required |
Returns:
| Type | Description |
|---|---|
ModalContentData
|
A |
Source code in src/codex_django/cabinet/presenters/modal.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | |
Functions
present_modal_state(state, action_url_resolver=None)
Present a generic modal state object as cabinet modal content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
Any
|
Mapping-like or attribute-based modal state. |
required |
action_url_resolver
|
ActionUrlResolver | None
|
Optional callable that resolves action URLs from raw action objects. |
None
|
Returns:
| Type | Description |
|---|---|
ModalContentData
|
A normalized |
Source code in src/codex_django/cabinet/presenters/modal.py
116 117 118 119 120 121 122 123 124 125 126 127 | |
Quick access helpers
codex_django.cabinet.quick_access
Helpers for configurable cabinet quick-access links.
Types — Navigation
codex_django.cabinet.types.nav
Navigation contracts for the cabinet two-space model.
This module defines the dataclasses that feature apps use to register
navigation entries in the cabinet shell. All types here are immutable
(frozen=True) to prevent accidental mutation of the global registry.
Two-space model
- staff space (
/cabinet/) — owners and administrators. Uses :class:TopbarEntryfor the top navigation bar and :class:SidebarItemfor the per-module sub-navigation. - client space (
/cabinet/my/) — end-customers. Uses :class:SidebarItemonly (no topbar dropdowns).
Typical usage in a feature app::
# features/booking/cabinet.py
from codex_django.cabinet import declare, TopbarEntry, SidebarItem, Shortcut
declare(
space="staff",
module="booking",
topbar=TopbarEntry(
group="services",
label="Booking",
icon="bi-calendar-check",
url="/cabinet/booking/",
order=10,
),
sidebar=[
SidebarItem(label="Schedule", url="booking:schedule", icon="bi-calendar3"),
SidebarItem(label="New Booking", url="booking:new", icon="bi-plus-circle"),
SidebarItem(label="Pending", url="booking:pending", icon="bi-hourglass-split",
badge_key="pending_count"),
],
shortcuts=[
Shortcut(label="New", url="booking:new", icon="bi-plus"),
],
)
declare(
space="client",
module="booking",
sidebar=[
SidebarItem(label="My Appointments", url="booking:my_bookings",
icon="bi-calendar2-check"),
],
)
Classes
TopbarEntry
dataclass
A navigation entry rendered inside a staff topbar dropdown group.
The staff topbar contains two dropdown menus: admin (for management
pages like analytics, users, and settings) and services (for feature
modules like booking, catalog, etc.). Each :class:TopbarEntry declares
which dropdown it belongs to via the group field.
Entries within the same group are sorted ascending by order.
This class is frozen — instances are immutable after creation.
Attributes:
| Name | Type | Description |
|---|---|---|
group |
str
|
Dropdown group identifier. Must be |
label |
str | Promise
|
Human-readable display name shown in the topbar link. |
icon |
str
|
Bootstrap Icons class name, e.g. |
url |
str
|
Absolute URL or named URL string resolved by the template. |
order |
int
|
Sort order within the group. Lower values appear first.
Defaults to |
Raises:
| Type | Description |
|---|---|
ImproperlyConfigured
|
If |
Example::
TopbarEntry(
group="services",
label="Booking",
icon="bi-calendar-check",
url="/cabinet/booking/",
order=10,
)
Source code in src/codex_django/cabinet/types/nav.py
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | |
SidebarItem
dataclass
A sub-navigation link rendered in the cabinet module sidebar.
When a view sets request.cabinet_module = "booking", the context
processor fetches SidebarItem list registered for that space+module
combination and exposes it as cabinet_sidebar. The _sidebar_staff.html
or _sidebar_client.html template then renders each item via
{% include "cabinet/includes/_nav_item.html" %}.
Items are sorted ascending by order at registration time.
This class is frozen — instances are immutable after creation.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str | Promise
|
Display name of the navigation link. |
url |
str
|
Named URL (e.g. |
icon |
str
|
Bootstrap Icons class name, e.g. |
badge_key |
str
|
Context variable name to read a numeric badge count from.
If the context contains |
order |
int
|
Sort order within the sidebar. Lower values appear first.
Defaults to |
permissions |
tuple[str, ...]
|
Tuple of Django permission strings. The item is hidden unless the user has at least one of the listed permissions. Empty tuple (default) means visible to all authenticated users. |
Example::
SidebarItem(
label="Pending",
url="booking:pending",
icon="bi-hourglass-split",
badge_key="pending_count",
order=3,
permissions=("booking.view_appointment",),
)
Source code in src/codex_django/cabinet/types/nav.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
Shortcut
dataclass
A quick-action link rendered in the staff topbar for the active module.
Shortcuts appear as small icon+label buttons in the topbar when the user is inside a specific cabinet module. They provide one-click access to the most common actions (e.g. "New Booking", "Add Client").
Shortcuts are registered per space+module via :func:codex_django.cabinet.declare
and exposed to templates as cabinet_shortcuts.
This class is frozen — instances are immutable after creation.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str | Promise
|
Short display name, e.g. |
url |
str
|
Named URL or absolute path for the link target. |
icon |
str
|
Bootstrap Icons class name, e.g. |
Example::
Shortcut(label="New Booking", url="booking:new", icon="bi-plus-circle")
Source code in src/codex_django/cabinet/types/nav.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | |
Types — Widgets
codex_django.cabinet.types.widgets
Data contracts for dashboard widget payloads.
This module defines the dataclasses that dashboard selectors return and
widget templates consume. Widgets live in
cabinet/templates/cabinet/widgets/ and are registered via
:class:~codex_django.cabinet.types.registry.DashboardWidget.
The typical data flow is::
selector function → returns widget payload dataclass
↓
DashboardSelector cache layer
↓
context processor injects into template context
↓
dashboard/index.html renders {% include widget.template %}
↓
widget template reads typed payload from context
Widget types:
- :class:
MetricWidgetData— single KPI number with optional trend. - :class:
TableWidgetData— tabular data with typed column definitions. - :class:
ListWidgetData— vertical list of labelled items with optional avatars.
Supporting types:
- :class:
TableColumn— column descriptor reused by both :class:TableWidgetData(dashboard) and :class:~codex_django.cabinet.types.components.DataTableData(page component). - :class:
ListItem— single row in a :class:ListWidgetData.
Classes
TableColumn
dataclass
Descriptor for a single column in a table widget or data table component.
Shared by :class:TableWidgetData (dashboard widget) and
:class:~codex_django.cabinet.types.components.DataTableData (full-page
component). The template uses col.key to read the corresponding value
from each row dict.
Attributes:
| Name | Type | Description |
|---|---|---|
key |
str
|
Dict key used to look up the cell value in each row.
Example: |
label |
str
|
Column header text shown to the user. |
align |
str
|
Horizontal text alignment. One of |
bold |
bool
|
If |
muted |
bool
|
If |
sortable |
bool
|
If |
badge_key |
str | None
|
If set, the cell value is rendered as a coloured badge.
The value of this attribute is the context key that maps the cell
value to a Bootstrap colour name (e.g. |
icon_key |
str | None
|
If set, an icon is prepended to the cell value. The value
is the context key for a Bootstrap Icons class mapping.
Defaults to |
Example::
TableColumn(key="status", label="Статус", badge_key="status_color_map")
TableColumn(key="name", label="Клиент", bold=True)
TableColumn(key="amount", label="Сумма", align="right")
Source code in src/codex_django/cabinet/types/widgets.py
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
ListItem
dataclass
A single row in a dashboard list widget.
Used as elements of :attr:ListWidgetData.items. Each item displays
a primary label, a primary value, and optional secondary lines and avatar.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
Primary descriptor on the left side, e.g. client name or service title. |
value |
str
|
Primary metric on the right side, e.g. |
avatar |
str | None
|
Optional URL to an avatar image, or a 1–2 character initials
string. When set, an avatar circle is rendered before the label.
Defaults to |
sublabel |
str | None
|
Optional secondary text below |
subvalue |
str | None
|
Optional secondary text below |
Example::
ListItem(label="Иван Петров", value="5 сеансов", avatar="ИП",
sublabel="Постоянный клиент")
Source code in src/codex_django/cabinet/types/widgets.py
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | |
MetricWidgetData
dataclass
Payload for a KPI metric widget (cabinet/widgets/kpi.html).
Displays a single headline number with an optional trend indicator (up/down/neutral arrow + label) and a decorative icon.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
Short description of the metric, e.g. |
value |
str
|
Formatted metric value, e.g. |
unit |
str | None
|
Optional unit suffix displayed after |
trend_value |
str | None
|
Formatted change value, e.g. |
trend_label |
str | None
|
Short context for the trend, e.g. |
trend_direction |
str
|
Visual direction of the trend arrow. One of
|
icon |
str | None
|
Bootstrap Icons class name for the decorative icon,
e.g. |
Example::
MetricWidgetData(
label="Новые клиенты",
value="142",
trend_value="+12%",
trend_label="за неделю",
trend_direction="up",
icon="bi-people",
)
Source code in src/codex_django/cabinet/types/widgets.py
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | |
TableWidgetData
dataclass
Payload for a table dashboard widget (cabinet/widgets/table.html).
Renders a compact data table inside a dashboard card. For a full-page
sortable/filterable table, use
:class:~codex_django.cabinet.types.components.DataTableData instead.
Attributes:
| Name | Type | Description |
|---|---|---|
columns |
list[TableColumn]
|
Ordered list of :class: |
rows |
list[dict[str, Any]]
|
List of dicts where each key corresponds to a
:attr: |
Example::
TableWidgetData(
columns=[
TableColumn(key="name", label="Клиент", bold=True),
TableColumn(key="date", label="Дата"),
TableColumn(key="amount", label="Сумма", align="right"),
],
rows=[
{"name": "Иван П.", "date": "01.04.2026", "amount": "3 500 ₽"},
{"name": "Анна С.", "date": "31.03.2026", "amount": "1 200 ₽"},
],
)
Source code in src/codex_django/cabinet/types/widgets.py
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | |
ListWidgetData
dataclass
Payload for a list dashboard widget (cabinet/widgets/list.html).
Renders a vertical list of labelled items with optional avatar circles
inside a dashboard card. For a full-page list, use
:class:~codex_django.cabinet.types.components.ListViewData instead.
Attributes:
| Name | Type | Description |
|---|---|---|
items |
list[ListItem]
|
Ordered list of :class: |
title |
str | None
|
Optional card title rendered above the list.
Defaults to |
Example::
ListWidgetData(
title="Топ клиенты",
items=[
ListItem(label="Иван Петров", value="12 000 ₽", avatar="ИП"),
ListItem(label="Анна Смирнова", value="8 500 ₽", avatar="АС"),
],
)
Source code in src/codex_django/cabinet/types/widgets.py
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | |
Types — Page Components
codex_django.cabinet.types.components
Data contracts for cabinet content-component templates.
Each dataclass in this module defines the payload that a template in
cabinet/templates/cabinet/components/ expects to receive via Django's
{% include ... with obj=obj %} tag.
Design principles:
- Backend computes, template renders. All positioning, counts, and formatting are calculated by the view or selector before the template sees them. Templates contain no business logic.
- One contract per component. Each
includereceives exactly one typed object. The template variable name matches the parameter name documented in each class. - Alpine.js for client-side state only. Toggle states (grid/list view,
search filter) live in
x-datainside the template. No custom JS files. - HTMX for server interactions. Detail panels, modals, and actions
use
hx-get/hx-post. URLs are passed through the contract. - Modal dispatch pattern. Components do not embed modals. They
dispatch
$dispatch('open-modal', {url: '...'})and the page-level_modal_base.htmlhandles loading.
Available components:
- :class:
DataTableData→components/data_table.html - :class:
CalendarGridData+ :class:CalendarSlot→components/calendar_grid.html - :class:
CardGridData+ :class:CardItem→components/card_grid.html - :class:
ListViewData+ :class:ListRow→components/list_view.html - :class:
SplitPanelData+ :class:ListRow→components/split_panel.html
Classes
TableFilter
dataclass
A single filter tab or button for :class:DataTableData.
Filter buttons are rendered as a row of toggle buttons above the table.
Clicking a filter sets activeFilter in Alpine state; the table
rows whose status field matches value remain visible.
Attributes:
| Name | Type | Description |
|---|---|---|
key |
str
|
Machine-readable identifier for the filter, e.g. |
label |
str
|
Human-readable button label, e.g. |
value |
str
|
The value to compare against the row's status field. Empty string (default) acts as "show all". |
Example::
TableFilter(key="pending", label="Ожидают", value="pending")
Source code in src/codex_django/cabinet/types/components.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | |
TableAction
dataclass
A row-level action link in a data table or list component.
Each action renders as a button or link in the last column of the table
(or in the row's action area). The URL for the action is read from the
row dict using url_key as the lookup key.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
Button label text, e.g. |
url_key |
str
|
Key in the row dict whose value is used as the action URL.
Example: if |
icon |
str
|
Bootstrap Icons class name, e.g. |
style |
str
|
Visual style. One of:
|
Example::
TableAction(label="Открыть", url_key="detail_url", icon="bi-eye")
TableAction(label="Удалить", url_key="delete_url", icon="bi-trash",
style="btn-danger")
Source code in src/codex_django/cabinet/types/components.py
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | |
DataTableData
dataclass
Payload contract for components/data_table.html.
Renders a searchable, filterable data table. Search and filter state is managed client-side by Alpine.js (no page reload). Row actions open detail modals via HTMX dispatch.
Template variable: table::
{% include "cabinet/components/data_table.html" with table=table %}
Attributes:
| Name | Type | Description |
|---|---|---|
columns |
Sequence[TableColumn | dict[str, Any]]
|
Ordered list of :class: |
rows |
Sequence[dict[str, Any]]
|
List of dicts. Each dict must contain keys matching
:attr: |
filters |
list[TableFilter]
|
Optional list of :class: |
actions |
list[TableAction]
|
Optional list of :class: |
search_placeholder |
str
|
Placeholder text for the search input. Empty string (default) hides the search bar entirely. |
empty_message |
str
|
Message shown when |
Example::
from codex_django.cabinet import DataTableData, TableColumn, TableFilter, TableAction
table = DataTableData(
columns=[
TableColumn(key="name", label="Клиент", bold=True),
TableColumn(key="date", label="Дата"),
TableColumn(key="status", label="Статус", badge_key="status_colors"),
],
rows=list(Appointment.objects.values("name", "date", "status", "detail_url")),
filters=[
TableFilter(key="pending", label="Ожидают", value="pending"),
TableFilter(key="confirmed", label="Подтверждены", value="confirmed"),
],
actions=[TableAction(label="Открыть", url_key="detail_url", icon="bi-eye")],
search_placeholder="Поиск клиентов...",
)
return render(request, "booking/appointments.html", {"table": table})
Source code in src/codex_django/cabinet/types/components.py
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
CalendarSlot
dataclass
A single event positioned on a :class:CalendarGridData grid.
The grid uses CSS grid-column and grid-row to place events.
All positional values are zero-based indexes into
:attr:CalendarGridData.cols and :attr:CalendarGridData.rows.
The backend computes these values; the template only renders them.
Attributes:
| Name | Type | Description |
|---|---|---|
col |
int
|
Zero-based column index. Corresponds to the master, room, or
day at that position in :attr: |
row |
int
|
Zero-based row index. Corresponds to the time slot at that
position in :attr: |
span |
int
|
Number of consecutive rows the event occupies. Compute as::
For a 1-hour appointment with 30-minute slots: |
title |
str
|
Primary event text (e.g. client name or appointment type). |
subtitle |
str
|
Optional secondary text (e.g. service name). Defaults to empty string. |
color |
str
|
CSS color value or Bootstrap |
url |
str
|
HTMX endpoint to load when the event is clicked. Opens the
detail/edit form in |
Example::
# 60-minute appointment starting at 9:00 with 30-min slots (row index 2)
CalendarSlot(
col=0, # first master
row=2, # 9:00 slot (rows: ["8:00","8:30","9:00",...])
span=2, # 60 min / 30 min = 2
title="Иван П.",
subtitle="Стрижка",
color="#6366f1",
url="/booking/42/",
)
Source code in src/codex_django/cabinet/types/components.py
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | |
CalendarGridData
dataclass
Payload contract for components/calendar_grid.html.
Renders a CSS Grid-based scheduling calendar. Columns represent any grouping axis (masters, rooms, weekdays), rows represent time slots at any granularity. The same contract is shared by two templates:
calendar_grid.html— vertical grid (cols × rows).calendar_timeline.html— horizontal timeline (future).
The backend computes all event positions. The template renders a background grid of clickable empty slots and overlays events on top.
Template variable: calendar::
{% include "cabinet/components/calendar_grid.html" with calendar=calendar %}
{% include "cabinet/includes/_modal_base.html" %}
Attributes:
| Name | Type | Description |
|---|---|---|
cols |
Sequence[str | dict[str, Any]]
|
Column header labels. Can be master names, room names,
weekday names, or any list of strings.
Example: |
rows |
Sequence[str | dict[str, Any]]
|
Time slot labels in display order.
Example: |
events |
list[CalendarSlot]
|
List of :class: |
slot_height_px |
int
|
Height of each time slot row in pixels.
Adjust for denser or sparser grids. Defaults to |
new_event_url |
str
|
HTMX base URL for creating a new event.
When set, each empty slot becomes clickable and sends
|
Example::
from codex_django.cabinet import CalendarGridData, CalendarSlot
# Build 30-min slots from 8:00 to 20:00
rows = [f"{h}:{m:02d}" for h in range(8, 20) for m in (0, 30)]
calendar = CalendarGridData(
cols=["Мастер 1", "Мастер 2", "Мастер 3"],
rows=rows,
events=[
CalendarSlot(col=0, row=2, span=2,
title="Иван П.", subtitle="Стрижка",
color="#6366f1", url="/booking/42/"),
],
slot_height_px=40,
new_event_url="/booking/new/",
)
return render(request, "booking/schedule.html", {"calendar": calendar})
Source code in src/codex_django/cabinet/types/components.py
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | |
CardItem
dataclass
A single card in a :class:CardGridData collection.
Renders as a card tile in grid mode or as a list row in list mode.
The meta field carries icon+text pairs for secondary information
(e.g. phone number, appointment count, last visit date).
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Unique string identifier used as an HTML key. Should be the
primary key of the underlying model, e.g. |
title |
str
|
Primary card heading (e.g. client full name). |
subtitle |
str
|
Optional secondary line below the title (e.g. client category or role). Defaults to empty string. |
avatar |
str
|
URL to an avatar image, or 1–2 character initials string for the avatar circle. Defaults to empty string (no avatar). |
badge |
str
|
Short status label rendered as a badge, e.g. |
badge_style |
str
|
Bootstrap colour name for the badge background,
e.g. |
url |
str
|
Link target for the card. Can be a detail page URL or an HTMX endpoint. Defaults to empty string (non-clickable). |
meta |
list[tuple[str, str]]
|
List of |
Example::
CardItem(
id=str(client.pk),
title=client.full_name,
subtitle=client.category,
avatar=client.initials,
badge="VIP",
badge_style="warning",
url=f"/cabinet/clients/{client.pk}/",
meta=[
("bi-telephone", client.phone),
("bi-calendar", f"{client.visit_count} визитов"),
],
)
Source code in src/codex_django/cabinet/types/components.py
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | |
CardGridData
dataclass
Payload contract for components/card_grid.html.
Renders a collection of :class:CardItem instances either as a
Bootstrap grid of cards or as a compact list. The user can toggle
between modes client-side via Alpine.js (no page reload).
Template variable: cards::
{% include "cabinet/components/card_grid.html" with cards=cards %}
Attributes:
| Name | Type | Description |
|---|---|---|
items |
list[CardItem]
|
List of :class: |
view_mode |
str
|
Initial render mode. |
search_placeholder |
str
|
Placeholder text for the client-side search
input. Alpine.js filters cards by |
empty_message |
str
|
Message shown when |
Example::
from codex_django.cabinet import CardGridData, CardItem
cards = CardGridData(
items=[
CardItem(
id=str(c.pk),
title=c.full_name,
avatar=c.initials,
url=f"/cabinet/clients/{c.pk}/",
)
for c in Client.objects.all()
],
search_placeholder="Поиск клиентов...",
)
return render(request, "clients/index.html", {"cards": cards})
Source code in src/codex_django/cabinet/types/components.py
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | |
ListRow
dataclass
A single row in a :class:ListViewData or :class:SplitPanelData.
Displays a primary label, optional secondary text, and optional meta information. Rows can be clickable (HTMX detail load or plain link) and carry per-row action buttons.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Unique string identifier (typically the model's primary key). |
primary |
str
|
Main row text, e.g. subject line or client name. |
secondary |
str
|
Optional secondary text rendered smaller below
|
meta |
str
|
Optional short metadata shown on the right (e.g. date, count). Defaults to empty string. |
avatar |
str
|
URL or initials string for the avatar circle. Defaults to empty string (no avatar). |
url |
str
|
HTMX endpoint or plain link URL. In :class: |
actions |
list[TableAction]
|
Per-row :class: |
Example::
ListRow(
id=str(msg.pk),
primary=msg.subject,
secondary=msg.sender,
meta=msg.created_at.strftime("%d.%m"),
url=f"/cabinet/conversations/{msg.pk}/",
)
Source code in src/codex_django/cabinet/types/components.py
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | |
ListViewData
dataclass
Payload contract for components/list_view.html.
Renders a vertical list of :class:ListRow instances with an optional
search bar. Clicking a row either opens a modal (HTMX) or navigates
to the row's URL. Row-level action buttons appear on hover.
Template variable: list::
{% include "cabinet/components/list_view.html" with list=list_data %}
{% include "cabinet/includes/_modal_base.html" %}
Attributes:
| Name | Type | Description |
|---|---|---|
rows |
list[ListRow]
|
Ordered list of :class: |
search_placeholder |
str
|
Placeholder text for the Alpine.js search
input. Filters rows by |
empty_message |
str
|
Message shown when |
Example::
from codex_django.cabinet import ListViewData, ListRow
list_data = ListViewData(
rows=[
ListRow(
id=str(n.pk),
primary=n.subject,
secondary=n.channel,
meta=n.sent_at.strftime("%d.%m %H:%M"),
url=f"/cabinet/notifications/{n.pk}/",
)
for n in Notification.objects.order_by("-sent_at")
],
search_placeholder="Поиск уведомлений...",
)
return render(request, "notifications/log.html", {"list": list_data})
Source code in src/codex_django/cabinet/types/components.py
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 | |
SplitPanelData
dataclass
Payload contract for components/split_panel.html.
Renders a two-column layout: a scrollable item list on the left and a
detail area on the right. Clicking a list item loads its detail via
HTMX into #split-panel-detail without a full page reload.
On mobile (< 768 px) the panels stack vertically.
Template variable: panel::
{% include "cabinet/components/split_panel.html" with panel=panel %}
Note
Place {% include "cabinet/includes/_modal_base.html" %} on the
page if the detail area opens sub-modals.
Attributes:
| Name | Type | Description |
|---|---|---|
items |
list[ListRow]
|
List of :class: |
active_id |
str
|
|
detail_url |
str
|
HTMX base URL for loading item details. The component
appends |
empty_message |
str
|
Message shown in the right panel when no item is
selected. Defaults to |
Example::
from codex_django.cabinet import SplitPanelData, ListRow
panel = SplitPanelData(
items=[
ListRow(
id=str(conv.pk),
primary=conv.subject,
secondary=conv.last_message[:60],
meta=conv.updated_at.strftime("%d.%m"),
avatar=conv.client_initials,
)
for conv in conversations
],
active_id=str(active_conversation.pk) if active_conversation else "",
detail_url="/cabinet/conversations",
empty_message="Выберите переписку",
)
return render(request, "conversations/index.html", {"panel": panel})
Source code in src/codex_django/cabinet/types/components.py
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 | |
ServiceItem
dataclass
A single service or product in a selector.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Unique ID (e.g. PK). |
title |
str
|
Display name (e.g. "Маникюр + Гель-лак"). |
price |
str
|
Price string (e.g. "45"). |
duration |
int
|
Duration in minutes (e.g. 90). |
category |
str
|
Machine name for filtering (e.g. "nails"). |
Source code in src/codex_django/cabinet/types/components.py
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | |
ServiceSelectorData
dataclass
Payload for components/widgets/service_selector.html.
Attributes:
| Name | Type | Description |
|---|---|---|
items |
list[ServiceItem]
|
List of :class: |
categories |
list[tuple[str, str]]
|
Ordered list of |
search_placeholder |
str
|
Placeholder for the search input. |
Source code in src/codex_django/cabinet/types/components.py
598 599 600 601 602 603 604 605 606 607 608 609 610 | |
ClientSelectorData
dataclass
Payload for components/widgets/client_selector.html.
Attributes:
| Name | Type | Description |
|---|---|---|
clients |
list[dict[str, Any]]
|
List of existing clients for search/selection. |
search_placeholder |
str
|
Placeholder for the search input. |
Source code in src/codex_django/cabinet/types/components.py
613 614 615 616 617 618 619 620 621 622 623 | |
DateTimePickerData
dataclass
Payload for components/widgets/date_time_picker.html.
Attributes:
| Name | Type | Description |
|---|---|---|
available_days |
list[dict[str, Any]]
|
List of days to show in the mini-calendar. |
calendar_cells |
list[dict[str, Any]]
|
Full calendar grid cells including blank placeholders. |
time_slots |
list[str]
|
List of time strings (e.g. "08:00"). |
busy_slots |
list[str]
|
List of time strings that are unavailable. |
current_month |
str
|
Month label (e.g. "March 2026"). |
Source code in src/codex_django/cabinet/types/components.py
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 | |
BookingSummaryData
dataclass
Payload for components/widgets/summary_panel.html.
Attributes:
| Name | Type | Description |
|---|---|---|
confirm_url |
str
|
POST endpoint for the booking. |
reset_url |
str
|
URL to clear the selection. |
masters |
list[dict[str, Any]]
|
List of available specialists for manual assignment. |
Source code in src/codex_django/cabinet/types/components.py
651 652 653 654 655 656 657 658 659 660 661 662 663 | |
BookingQuickCreateServiceOption
dataclass
Service option shown inside a quick-create booking modal.
Source code in src/codex_django/cabinet/types/components.py
666 667 668 669 670 671 672 673 | |
BookingQuickCreateClientOption
dataclass
Client option shown inside a quick-create booking modal.
Source code in src/codex_django/cabinet/types/components.py
676 677 678 679 680 681 682 683 684 | |
BookingQuickCreateData
dataclass
Payload for a quick-create single-appointment block inside a modal.
Source code in src/codex_django/cabinet/types/components.py
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | |
BookingSlotPickerOption
dataclass
Single slot option in a booking slot-picker block.
Source code in src/codex_django/cabinet/types/components.py
710 711 712 713 714 715 716 | |
BookingSlotPickerData
dataclass
Payload for a booking-native date navigation and slot selection block.
Source code in src/codex_django/cabinet/types/components.py
719 720 721 722 723 724 725 726 727 728 729 730 | |
BookingChainPreviewItem
dataclass
Single row in a booking chain preview block.
Source code in src/codex_django/cabinet/types/components.py
733 734 735 736 737 738 739 | |
BookingChainPreviewData
dataclass
Payload for a booking chain preview block.
Source code in src/codex_django/cabinet/types/components.py
742 743 744 745 746 747 | |
ModalSection
dataclass
Base class for modal sections.
Source code in src/codex_django/cabinet/types/components.py
755 756 757 758 759 | |
ProfileSection
dataclass
Bases: ModalSection
Client/Staff profile header in a modal.
Source code in src/codex_django/cabinet/types/components.py
762 763 764 765 766 767 768 769 | |
SummarySection
dataclass
Bases: ModalSection
Grid of key-value pairs (e.g. Appointment details).
Source code in src/codex_django/cabinet/types/components.py
778 779 780 781 782 783 | |
FormSection
dataclass
Bases: ModalSection
Input fields group.
Source code in src/codex_django/cabinet/types/components.py
796 797 798 799 800 801 | |
ModalAction
dataclass
A button in the modal footer or action group.
Source code in src/codex_django/cabinet/types/components.py
804 805 806 807 808 809 810 811 812 | |
ActionSection
dataclass
Bases: ModalSection
Group of action buttons.
Source code in src/codex_django/cabinet/types/components.py
815 816 817 818 819 820 | |
SlotPickerSection
dataclass
Bases: ModalSection
Booking-native date navigator and slot picker for modal workflows.
Source code in src/codex_django/cabinet/types/components.py
823 824 825 826 827 828 829 830 | |
QuickCreateSection
dataclass
Bases: ModalSection
Booking quick-create block for creating one appointment from a calendar slot.
Source code in src/codex_django/cabinet/types/components.py
833 834 835 836 837 838 839 840 | |
ChainPreviewSection
dataclass
Bases: ModalSection
Booking chain preview block used by richer booking builders/modals.
Source code in src/codex_django/cabinet/types/components.py
843 844 845 846 847 848 | |
ModalContentData
dataclass
Payload for components/generic_modal.html.
Assembles a modal window from multiple functional sections.
Source code in src/codex_django/cabinet/types/components.py
851 852 853 854 855 856 857 858 859 | |
Types — Registry
codex_django.cabinet.types.registry
Registration contracts for the cabinet registry.
This module defines the dataclasses used when feature apps register
dashboard widgets and navigation sections with the global
:data:~codex_django.cabinet.registry.cabinet_registry.
Types:
- :class:
DashboardWidget— declares a widget to render on the dashboard. - :class:
NavAction— a generic action link in cabinet navigation areas. - :class:
CabinetSection— deprecated v1 navigation section. Use :class:~codex_django.cabinet.types.nav.TopbarEntrywith the newdeclare(space=...)API instead.
Classes
DashboardWidget
dataclass
Declaration of a dashboard widget contributed by a feature app.
Feature apps register widgets via :func:~codex_django.cabinet.declare.
The registry stores them and the dashboard view renders all widgets
that the current user has permission to see.
The template path is resolved relative to Django's template loaders.
Widget templates live in cabinet/templates/cabinet/widgets/ for
built-in widgets, or in the feature app's own templates/ directory
for custom widgets.
This class is frozen — instances are immutable after creation.
Attributes:
| Name | Type | Description |
|---|---|---|
template |
str
|
Django template path, e.g.
|
col |
str
|
Bootstrap column class controlling widget width.
Defaults to |
lazy |
bool
|
If |
nav_group |
str
|
Navigation group this widget belongs to. Determines which dashboard tab shows the widget. Must be one of:
|
permissions |
tuple[str, ...]
|
Tuple of Django permission strings. The widget is hidden unless the user has at least one listed permission. Empty tuple (default) means visible to all authenticated users. |
order |
int
|
Sort order on the dashboard. Lower values appear first.
Defaults to |
Raises:
| Type | Description |
|---|---|
ImproperlyConfigured
|
If |
Example::
DashboardWidget(
template="booking/widgets/upcoming_appointments.html",
col="col-lg-6",
lazy=True,
nav_group="services",
permissions=("booking.view_appointment",),
order=10,
)
Source code in src/codex_django/cabinet/types/registry.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
NavAction
dataclass
A generic action link rendered in cabinet navigation areas.
Used for supplementary links that do not belong to the main topbar or sidebar navigation — for example, footer links, context-specific quick actions, or legacy v1 topbar buttons.
This class is frozen — instances are immutable after creation.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
Display name of the action, e.g. |
url |
str
|
Absolute URL or named URL string for the link. |
icon |
str | None
|
Optional Bootstrap Icons class name, e.g. |
Example::
NavAction(label="Настройки", url="/cabinet/settings/", icon="bi-gear")
Source code in src/codex_django/cabinet/types/registry.py
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
CabinetModuleConfig
dataclass
Read-only snapshot of a registered cabinet module.
This is the public alternative to inspecting CabinetRegistry private
storage. Projects can use it to build custom shells, quick access UIs, and
module-aware views without depending on internal dict layout.
Source code in src/codex_django/cabinet/types/registry.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
CabinetSection
dataclass
Deprecated. V1 navigation section for the cabinet topbar.
.. deprecated::
Use :class:~codex_django.cabinet.types.nav.TopbarEntry together
with the new declare(space=...) API. CabinetSection is kept
only for backward compatibility with projects generated by older
versions of codex-django-cli.
In the v1 API a CabinetSection was passed directly to declare()::
# Old — do not use in new code
from codex_django.cabinet import declare, CabinetSection
declare(
module="booking",
section=CabinetSection(
label="Booking",
icon="bi-calendar",
nav_group="services",
url="/cabinet/booking/",
order=10,
),
)
Migrate to the v2 API::
# New
from codex_django.cabinet import declare, TopbarEntry, SidebarItem
declare(
space="staff",
module="booking",
topbar=TopbarEntry(
group="services",
label="Booking",
icon="bi-calendar",
url="/cabinet/booking/",
order=10,
),
sidebar=[...],
)
This class is frozen — instances are immutable after creation.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
Display name of the section. |
icon |
str
|
Bootstrap Icons class name, e.g. |
nav_group |
str
|
Dropdown group. Must be |
url |
str | None
|
Optional link URL. |
permissions |
tuple[str, ...]
|
Tuple of Django permission strings (OR logic).
Empty tuple means visible to all. Defaults to |
order |
int
|
Sort order. Defaults to |
Raises:
| Type | Description |
|---|---|
ImproperlyConfigured
|
If |
Source code in src/codex_django/cabinet/types/registry.py
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | |
Context processors
codex_django.cabinet.context_processors
Template context helpers for the cabinet UI shell.
Classes
Dashboard selector
codex_django.cabinet.selector.dashboard
Dashboard Selector
Single entry point for dashboard data providers.
Providers are registered through @extend and cached in Redis through
DashboardRedisManager using JSON rather than pickle.
Example project usage:
# cabinet/selector/dashboard.py inside the generated project
from codex_django.cabinet.selector.dashboard import DashboardSelector
from ..mock import CabinetMockData
@DashboardSelector.extend(cache_key="base_stats", cache_ttl=300)
def base_stats(request):
return {"dashboard_stats": CabinetMockData.get_dashboard_stats()}
Adding a provider from a feature module such as booking:
# features/booking/cabinet.py
@DashboardSelector.extend(cache_key="booking_kpi", cache_ttl=300)
def booking_kpi(request):
return {"booking_kpi": BookingSelector.get_dashboard_kpi(request)}
Invalidating cached data from a model save hook:
# features/booking/models/booking.py
from codex_django.cabinet.selector.dashboard import DashboardSelector
DashboardSelector.invalidate("booking_kpi")
Classes
DashboardAdapter
Bases: ABC
Base class for dashboard data adapters.
Adapters are responsible for fetching and formatting data for specific widget types before the selector merges them into the template context.
Source code in src/codex_django/cabinet/selector/dashboard.py
51 52 53 54 55 56 57 58 59 60 61 | |
Functions
get_data(request)
abstractmethod
Fetch and return widget data as a dictionary payload.
Source code in src/codex_django/cabinet/selector/dashboard.py
58 59 60 61 | |
MetricAdapter
Bases: DashboardAdapter
Generic adapter for metric widgets.
Source code in src/codex_django/cabinet/selector/dashboard.py
67 68 69 70 71 72 73 74 75 76 | |
Functions
get_data(request)
Wrap metric payloads under the metric key.
Source code in src/codex_django/cabinet/selector/dashboard.py
73 74 75 76 | |
TableAdapter
Bases: DashboardAdapter
Generic adapter for table widgets.
Source code in src/codex_django/cabinet/selector/dashboard.py
79 80 81 82 83 84 85 86 87 88 | |
Functions
get_data(request)
Wrap table payloads under the table key.
Source code in src/codex_django/cabinet/selector/dashboard.py
85 86 87 88 | |
ListAdapter
Bases: DashboardAdapter
Generic adapter for list widgets.
Source code in src/codex_django/cabinet/selector/dashboard.py
91 92 93 94 95 96 97 98 99 100 | |
Functions
get_data(request)
Wrap list payloads under the list key.
Source code in src/codex_django/cabinet/selector/dashboard.py
97 98 99 100 | |
DashboardSelector
Extensible dashboard data aggregator with Redis caching.
Each provider is a flat function (request) -> dict or a DashboardAdapter. Registered via @DashboardSelector.extend(cache_key=..., cache_ttl=...).
Source code in src/codex_django/cabinet/selector/dashboard.py
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | |
Functions
extend(fn_or_adapter=None, *, cache_key='', cache_ttl=120)
classmethod
extend(fn_or_adapter: TProvider, *, cache_key: str = '', cache_ttl: int = 120) -> TProvider
extend(fn_or_adapter: None = None, *, cache_key: str = '', cache_ttl: int = 120) -> Callable[[TProvider], TProvider]
Register a dashboard data provider.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cache_key
|
str
|
Redis key suffix. Defaults to function/class name. |
''
|
cache_ttl
|
int
|
Seconds to cache. 0 = no cache (real-time). |
120
|
Source code in src/codex_django/cabinet/selector/dashboard.py
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | |
get_context(request)
classmethod
Collect data from all registered providers. Each provider is read from Redis cache if available, otherwise called and cached.
Source code in src/codex_django/cabinet/selector/dashboard.py
171 172 173 174 175 176 177 178 179 180 181 | |
invalidate(cache_key)
classmethod
Invalidate cache for a specific provider. Call from model signals.
Source code in src/codex_django/cabinet/selector/dashboard.py
202 203 204 205 | |
invalidate_all()
classmethod
Invalidate all dashboard provider caches.
Source code in src/codex_django/cabinet/selector/dashboard.py
207 208 209 210 | |
Functions
get_dashboard_redis_manager()
cached
Return the dashboard cache manager lazily.
Source code in src/codex_django/cabinet/selector/dashboard.py
45 46 47 48 | |
Dashboard Redis manager
codex_django.cabinet.redis.managers.dashboard
Dashboard Redis Manager
Redis cache for dashboard data. Each provider is stored under its own key.
Serialization uses JSON instead of pickle, which keeps values readable from
redis-cli. Supported value types are str, int, float, bool,
list, dict, and Decimal converted to str.
Key format: {PROJECT_NAME}:cabinet:dashboard:{provider_key}
Classes
DashboardRedisManager
Bases: BaseDjangoRedisManager
Per-provider Redis cache for dashboard data.
Usage in DashboardSelector
manager = DashboardRedisManager()
cached = manager.get("booking_kpi") if cached is None: data = expensive_query() manager.set("booking_kpi", data, ttl=300)
Invalidation from model signal / lifecycle hook: manager.invalidate("booking_kpi") # one provider manager.invalidate_all() # full dashboard refresh
Source code in src/codex_django/cabinet/redis/managers/dashboard.py
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | |
Functions
aget(provider_key)
async
Return cached provider data or None on cache miss.
Source code in src/codex_django/cabinet/redis/managers/dashboard.py
71 72 73 74 75 76 77 78 79 80 81 82 | |
get(provider_key)
Synchronously return cached provider data.
Source code in src/codex_django/cabinet/redis/managers/dashboard.py
84 85 86 87 88 89 90 91 92 93 94 95 | |
aset(provider_key, data, ttl)
async
Store provider data with a TTL in seconds.
Source code in src/codex_django/cabinet/redis/managers/dashboard.py
99 100 101 102 103 104 105 106 107 108 | |
set(provider_key, data, ttl)
Synchronously store provider data with a TTL in seconds.
Source code in src/codex_django/cabinet/redis/managers/dashboard.py
110 111 112 113 114 115 116 117 118 119 | |
ainvalidate(provider_key)
async
Delete cache for a single provider.
Source code in src/codex_django/cabinet/redis/managers/dashboard.py
123 124 125 126 127 128 | |
invalidate(provider_key)
Synchronously delete cache for a single provider.
Source code in src/codex_django/cabinet/redis/managers/dashboard.py
130 131 132 133 134 135 | |
ainvalidate_all()
async
Delete all dashboard provider caches using a key pattern.
Source code in src/codex_django/cabinet/redis/managers/dashboard.py
137 138 139 140 141 142 143 | |
invalidate_all()
Synchronously delete all dashboard provider caches.
Source code in src/codex_django/cabinet/redis/managers/dashboard.py
145 146 147 148 149 150 151 152 153 154 155 156 | |
Settings Redis manager
codex_django.cabinet.redis.managers.settings
Redis manager for cabinet settings payloads.
Classes
CabinetSettingsRedisManager
Bases: BaseDjangoRedisManager
Sync/async manager for CabinetSettings in Redis.
Key format: {PROJECT_NAME}:cabinet:settings Uses Redis Hash (same pattern as DjangoSiteSettingsManager). Respects DEBUG mode + CODEX_REDIS_ENABLED flag — safe for local dev without Redis.
Source code in src/codex_django/cabinet/redis/managers/settings.py
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | |
Functions
aget()
async
Asynchronously return the cached cabinet settings payload.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Cached cabinet settings data, or an empty dictionary when caching |
dict[str, Any]
|
is disabled or the Redis hash does not exist. |
Source code in src/codex_django/cabinet/redis/managers/settings.py
21 22 23 24 25 26 27 28 29 30 31 | |
get()
Synchronously return the cached cabinet settings payload.
Source code in src/codex_django/cabinet/redis/managers/settings.py
33 34 35 36 37 38 | |
asave_instance(instance)
async
Asynchronously persist a cabinet settings instance to Redis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
instance
|
Any
|
Object implementing |
required |
Source code in src/codex_django/cabinet/redis/managers/settings.py
40 41 42 43 44 45 46 47 48 49 50 51 | |
save_instance(instance)
Synchronously persist a cabinet settings instance to Redis.
Source code in src/codex_django/cabinet/redis/managers/settings.py
53 54 55 56 57 58 59 60 | |
ainvalidate()
async
Asynchronously invalidate the cached cabinet settings payload.
Source code in src/codex_django/cabinet/redis/managers/settings.py
62 63 64 65 66 67 | |
invalidate()
Synchronously invalidate the cached cabinet settings payload.
Source code in src/codex_django/cabinet/redis/managers/settings.py
69 70 71 72 73 74 | |
Settings model
codex_django.cabinet.models.settings
Singleton-like cabinet settings model and Redis synchronization hooks.
Classes
CabinetSettings
Bases: LifecycleModelMixin, Model
Cabinet settings — Singleton (always one instance, pk=1).
Stores cabinet-level configuration: name, logo, theme overrides. Auto-syncs to Redis on save via django_lifecycle hook. Skips Redis sync in DEBUG mode unless CODEX_REDIS_ENABLED=True.
Source code in src/codex_django/cabinet/models/settings.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | |
Functions
__str__()
Return the display name used in admin and debug output.
Source code in src/codex_django/cabinet/models/settings.py
25 26 27 | |
save(*args, **kwargs)
Persist the singleton settings row under primary key 1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Positional arguments forwarded to |
()
|
**kwargs
|
Any
|
Keyword arguments forwarded to |
{}
|
Source code in src/codex_django/cabinet/models/settings.py
29 30 31 32 33 34 35 36 37 | |
delete(*args, **kwargs)
Refuse deletion so the singleton settings row always remains available.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
Unused positional arguments accepted for API compatibility. |
()
|
**kwargs
|
Any
|
Unused keyword arguments accepted for API compatibility. |
{}
|
Returns:
| Type | Description |
|---|---|
int
|
The tuple Django expects from |
dict[str, int]
|
no rows were removed. |
Source code in src/codex_django/cabinet/models/settings.py
39 40 41 42 43 44 45 46 47 48 49 50 | |
load()
classmethod
Load or create the singleton settings instance from the database.
Returns:
| Type | Description |
|---|---|
CabinetSettings
|
The singleton :class: |
Source code in src/codex_django/cabinet/models/settings.py
52 53 54 55 56 57 58 59 60 | |
to_cabinet_dict()
Serialize the model to a Redis-friendly flat mapping.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A dictionary that can be stored in the cabinet settings Redis hash. |
Source code in src/codex_django/cabinet/models/settings.py
62 63 64 65 66 67 68 69 70 71 72 73 74 | |
sync_to_redis()
Persist the latest cabinet settings payload to Redis after save.
Source code in src/codex_django/cabinet/models/settings.py
76 77 78 79 80 81 82 83 84 85 86 | |
Shared model mixins
codex_django.cabinet.models.mixins
Field mixins for cabinet domain models.
These mixins define the expected interface (which fields are required), not the business logic (choices, validation). Developers declare their own status choices etc. on the concrete model.
Usage in project
from codex_django.cabinet.models import AppointmentFieldsMixin
class Appointment(AppointmentFieldsMixin): STATUS_PENDING = 'pending' STATUS_CONFIRMED = 'confirmed' STATUS_CHOICES = [(STATUS_PENDING, 'Pending'), (STATUS_CONFIRMED, 'Confirmed')] status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_PENDING)
Classes
AppointmentFieldsMixin
Bases: Model
Base fields mixin for appointments/bookings. No choices — developer defines them.
Source code in src/codex_django/cabinet/models/mixins.py
23 24 25 26 27 28 29 30 31 | |
ClientFieldsMixin
Bases: Model
Base fields mixin for client profiles.
Source code in src/codex_django/cabinet/models/mixins.py
34 35 36 37 38 39 40 41 42 43 44 45 | |
ServiceFieldsMixin
Bases: Model
Base fields mixin for services/offerings.
Source code in src/codex_django/cabinet/models/mixins.py
48 49 50 51 52 53 54 55 56 57 | |
Dashboard views
codex_django.cabinet.views.dashboard
Views for the main cabinet dashboard shell.
Classes
Functions
dashboard_view(request)
Render the dashboard page with aggregated provider context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
HttpRequest
|
Authenticated Django request for the cabinet dashboard. |
required |
Returns:
| Type | Description |
|---|---|
HttpResponse
|
Rendered dashboard response using the registered selector providers. |
Source code in src/codex_django/cabinet/views/dashboard.py
10 11 12 13 14 15 16 17 18 19 20 21 | |
Site settings views
codex_django.cabinet.views.site_settings
Views for cabinet site-settings pages and HTMX partials.
Functions
site_settings_view(request)
Отображает единую страницу всех настроек сайта и обрабатывает сохранение.
Source code in src/codex_django/cabinet/views/site_settings.py
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | |
site_settings_tab_view(request, tab_slug)
Устаревшая вьюха для вкладок, теперь просто редиректит на общий корень по якорю.
Source code in src/codex_django/cabinet/views/site_settings.py
42 43 44 45 | |