Дашборд
Главная страница — это набор виджетов, которые вы сами компонуете. Графики отрисовываются сервером — без библиотек для графиков, без canvas, и каждый из них указывает, во сколько запросов он обходится.
Доступно с версии v0.4.0
Что вы получаете без какой-либо настройки
Количество строк каждой модели, сгруппированное так же, как их группирует
боковая панель, и график новых аккаунтов за последние тридцать дней. Это два
виджета — Signups() и Counts() — именно таким дашборд был всегда.
Ничего из того, что ниже, не обязательно. Обращайтесь к этому, когда главная страница должна отвечать на вопрос о вашей предметной области, а не о схеме данных.
Компоновка
from fastfort.admin import Breakdown, Counts, Metric, Recent, Trend
fort.set_dashboard(
Metric(Shipment, title="Shipments booked", days=14, icon="truck"),
Metric(Invoice, title="Invoices raised", days=14, icon="credit-card"),
Trend(Shipment, title="Shipments per day", days=30),
Breakdown(Shipment, on="status"),
Recent(Shipment, limit=6),
Counts(),
)
Виджеты отрисовываются в заданном порядке. fort.set_dashboard() без
аргументов — это дашборд, на котором ничего нет; иногда это именно то, что
нужно, но получить это случайно — только запутает, поэтому это единственный
способ так сказать.
Пять встроенных виджетов
| Виджет | Что рисует | Во что обходится |
|---|---|---|
Metric(model) | Число, дельта со знаком и спарклайн | Один запрос на день |
Trend(model) | Тот же ряд, но в увеличенном виде — как график с областью или как столбцы — с итогом за период, самым загруженным днём и средним значением в день | Один запрос на день |
Breakdown(model, on="status") | Один индикатор на каждое значение столбца | Один запрос на значение |
Recent(model) | Самые новые строки со ссылками | Один запрос |
Counts() | Количество строк каждой зарегистрированной модели, сгруппированное по приложению | Один запрос на модель |
Каждый виджет принимает title=, span= (THIRD, HALF или FULL), а там,
где он строит график по времени, — days= и on=, столбец, фиксирующий
момент появления строки. Если on не указан, распознаются обычные имена:
created_at, date_joined, registered_at и другие.
Столбец стоимости — не украшение. Дашборд — самая часто открываемая
страница в любой админке, и легко допустить ошибку, когда карточка незаметно
выполняет сотню запросов. Trend(model, days=90) — это девяносто
индексированных подсчётов при каждой загрузке страницы; days=14 —
четырнадцать. Все виджеты на странице используют единый unit of work запроса,
поэтому счёт идёт на запросы, а не на соединения.
Виджет, которому нечего сказать, ничего не отрисовывает
Несуществующий столбец, модель без столбца с датой, разбивка по свободному
тексту, dashboard_days=0 — в каждом из этих случаев карточка просто не
отображается, а не выбрасывает исключение. Опечатка в конфигурационном файле
должна стоить одной карточки, но никогда — страницы, которую все открывают
первой.
Именно поэтому Breakdown отказывается работать со столбцом без
фиксированного набора значений: подсчёт различных значений столбца со
свободным текстом означает полное сканирование таблицы.
Графики отрисовываются сервером
SVG-путь, несколько <line>, и прямоугольники с заданной высотой. Никакой
библиотеки для графики, никакого <canvas>, никакого второго запроса и ни
единого лишнего байта JavaScript сверх того, что уже есть в админке.
Это значит: график присутствует уже при первой отрисовке страницы, он печатается, использует цвета текущей темы — как светлой, так и тёмной, — и переживает отключение скриптов. Скрипт добавляет только всплывающие подсказки и ничего больше. Рядом с каждым графиком те же данные представлены в виде таблицы — скрытой визуально, но доступной программам чтения с экрана.
Каждый график использует только один цвет, потому что вся палитра происходит от одного тона. Тренд — это один ряд данных, а разбивка сравнивает столбцы по длине — единственному визуальному каналу, который люди считывают точно, — с числом и долей в тексте рядом с каждым столбцом.
Написание собственного виджета
from fastfort.admin.dashboard import Card, Widget
class OpenTickets(Widget):
span = 1
async def resolve(self, context):
adapter = context.adapter_for(Ticket)
count = await adapter.count(ListQuery(filters=(Filter("state", FilterOperator.EXACT, "open"),)))
if not count:
return None
return Card(
template="dashboard/open_tickets.html",
context={"count": count},
span=self.span,
)
fort.set_dashboard(OpenTickets(), Counts())
Card.template разрешается через рендерер, причём собственная директория
шаблонов проекта проверяется раньше директории пакета — так что достаточно
разместить dashboard/open_tickets.html в вашей собственной директории
шаблонов. На странице нет никакой особой обработки для встроенных виджетов;
ваш виджет отрисовывается точно так же, как и они.
context предоставляет unit of work запроса (adapter_for, spec_for),
реестр (key_for, url_for, title_for) и настроенное окно (days).
Обращаться в обход него напрямую к ORM — это именно то, чего следует
избегать: виджет, написанный против протокола адаптера, продолжает работать
при смене бэкенда проекта.