Машинный перевод под контролем человека
Этот контент переведён с помощью машинной генерации, направляемой составленными людьми глоссариями и руководствами по стилю. Поскольку текст не проверяется вручную построчно, возможны отдельные ошибки или неестественные формулировки.
В случае любых расхождений авторитетным источником считается оригинальная версия на английском языке.
Шаблоны
Каждая страница административной панели — это шаблон Jinja2, который вы можете переопределить. Измените отдельную страницу списка, ячейку таблицы одного поля или виджет дашборда, не создавая форк встроенного дерева шаблонов.
Как работает загрузчик шаблонов
from sqlalchemy import create_engine
from starlette_admin.contrib.sqla import Admin
engine = create_engine("sqlite:///admin.sqlite")
admin = Admin(engine, title="My Admin", templates_dir="my_templates/")
Admin создает загрузчик Jinja2 ChoiceLoader, который сначала проверяет ваш templates_dir, а затем встроенный каталог пакета starlette_admin/templates/. Поместите файл в my_templates/ по тому же относительному пути, что и внутри starlette_admin/templates/, — и ваш файл затмит встроенный. Все остальные шаблоны продолжат рендериться из встроенного каталога.
Note
В цепочку загрузчиков также зарегистрирован PrefixLoader под ключом @starlette-admin, который всегда указывает на встроенные шаблоны независимо от того, какие из них затмены в templates_dir. Обращайтесь к ним по формату пути @starlette-admin/<name>.html, не добавляя завершающий слеш к самому префиксу. См. раздел Переопределение шаблона одной страницы ниже, чтобы понять, зачем это нужно.
Карта каталога шаблонов
| Путь | Для чего рендерится |
|---|---|
base.html |
Внешний HTML-каркас (<html>, <head>, скрипты) |
layout.html |
Боковая панель и верхняя панель (расширяет base.html) |
index.html |
Дашборд или домашняя страница |
list.html |
Страница списка модели (таблица, панель фильтров, пагинация) |
detail.html |
Представление детализации (только чтение) одной записи |
create.html |
Форма создания |
edit.html |
Форма редактирования |
login.html |
Страница входа |
error.html |
Страница HTTP-ошибки (403, 404 и т. д.) |
actions.html |
Модальное окно массовых действий |
row-actions.html |
Выпадающее меню действий для строки |
inline.html |
Inline formset на странице создания/редактирования |
inline_detail.html |
Inline-таблица на странице детализации |
inline_row.html |
Отдельная строка внутри inline formset |
_filter_bar.html |
Панель активных фильтров над списком |
_filter_builder.html |
Модальное окно конструктора фильтров |
_pagination.html |
Элементы управления пагинацией |
_column_header.html |
Ячейка заголовка сортируемого столбца |
_form_footer.html |
Кнопки «Сохранить», «Сохранить и продолжить» или «Добавить еще» |
_form_group.html |
Fieldset группы формы на форме создания/редактирования |
_form_group_fields.html |
Поля ввода, отображаемые внутри группы формы |
fields/list/<type>.html |
Ячейка столбца списка для типа поля |
fields/detail/<type>.html |
Отображение поля на странице детализации |
fields/form/<type>.html |
Виджет ввода для типа поля |
widgets/<name>.html |
Шаблон виджета дашборда |
modals/actions.html |
Модальное окно подтверждения действия |
modals/delete.html |
Модальное окно подтверждения удаления |
modals/error.html |
Модальное окно ошибки |
modals/import.html |
Модальное окно импорта |
macros/views.html |
Общие макросы Jinja2, используемые на разных страницах |
Note
Встроенное дерево также содержит modals/loading.html — универсальное модальное окно состояния загрузки — и несколько специализированных шаблонов полей в fields/list/, fields/detail/ и fields/form/. Перед переопределением универсального файла <type>.html проверьте точные имена файлов в starlette_admin/templates/ для установленной у вас версии.
Переопределение шаблона одной страницы
{# my_templates/list.html #}
{% extends "@starlette-admin/list.html" %}
{% block content %}
<div class="alert alert-info">Пользовательский баннер над списком.</div>
{{ super() }}
{% endblock %}
Запись {% extends "list.html" %} разрешилась бы обратно в ваш собственныйmy_templates/list.html, посколькуtemplates_dirпроверяется первым, а такая циклическая ссылка вызывает ошибку бесконечной рекурсии. Префикс@starlette-admin/всегда указывает на встроенную копию, поэтому каждыйextendsиinclude` внутри переопределения должен использовать именно его вместо имени файла без префикса.
Переопределяемые блоки
Каждая встроенная страница расширяет layout.html, который в свою очередь расширяет base.html. Вместо замены целого файла переопределите один {% block %}, чтобы изменить отдельный фрагмент, не дублируя остальную часть страницы:
{# my_templates/list.html #}
{% extends "@starlette-admin/list.html" %}
{% block list_toolbar_extra %}
{{ super() }}
<a class="btn btn-outline-primary" href="/reports/export">Пользовательский отчет</a>
{% endblock %}
base.html
| Блок | Содержимое |
|---|---|
favicon |
Тег favicon <link> |
title |
Тег <title> |
head_meta |
Теги <meta> внутри элемента <head> |
head_css |
Теги <link> стилей |
head |
Произвольная точка вставки внутри элемента <head> |
body |
Все содержимое <body> (этот блок переопределяется в layout.html) |
modal |
Точка вставки модальных окон на уровне страницы |
script |
Теги <script> непосредственно перед закрывающим тегом </body> |
tail |
Пустая точка вставки в самом конце <body>, после блока script |
layout.html
| Блок | Содержимое |
|---|---|
sidebar |
Весь элемент боковой панели <aside> (включая бренд, меню и футер) |
brand |
Изображение логотипа (или запасное значение app_title) внутри ссылки бренда боковой панели |
sidebar_menu |
Список ссылок на представления внутри боковой панели |
sidebar_footer |
Нижняя область боковой панели |
user_menu_trigger |
Аватар и имя пользователя на кнопке пользовательского меню. Определяется один раз и используется как в мобильной боковой панели, так и в десктопной навигационной панели через self.user_menu_trigger(), поэтому его переопределение обновляет оба места |
user_menu_items |
Пункты выпадающего меню в пользовательском меню |
navbar |
Верхняя навигационная панель |
navbar_extra |
Дополнительный контент в навигационной панели рядом с пользовательским меню |
header |
Область заголовка страницы, расположенная над блоком content (включает название и хлебные крошки) |
flash_messages |
Предназначенная область для отображения flash-сообщений |
content_before |
Точка вставки непосредственно перед блоком content |
content |
Основной контент страницы (это блок, заполняемый файлами list.html, detail.html и т. д.) |
content_after |
Точка вставки непосредственно после блока content |
page_footer |
Область футера под контентом страницы |
list.html
| Блок | Содержимое |
|---|---|
header |
Заголовок страницы (включает название и хлебные крошки) |
page_title |
Заголовок <h1> внутри шапки страницы |
breadcrumbs |
Хлебные крошки внутри шапки страницы |
modal |
Модальные окна удаления, действия и импорта |
content |
Полное содержимое страницы списка |
list_search |
Область поля поиска |
list_toolbar |
Ряд инструментов с кнопками фильтрации, экспорта, импорта и создания |
list_toolbar_extra |
Дополнительная точка вставки в самом конце панели инструментов |
list_before_table |
Точка вставки перед таблицей |
list_table |
Сам элемент <table> |
list_header |
Ряд <thead> с ячейками чекбокса и заголовков столбцов |
list_row |
Отдельный элемент <tr> в таблице результатов (scoped-блок; имеет доступ к row, row_pk и row_clickable) |
list_row_actions_before |
Ячейка действий строки при значении row_actions_position равном BEFORE_COLUMNS (scoped-блок) |
list_row_actions_after |
Ячейка действий строки при значении row_actions_position равном AFTER_COLUMNS (scoped-блок) |
list_empty |
Заглушка «Нет данных» (scoped-блок, рендерится для пустых состояний) |
list_after_table |
Точка вставки после таблицы |
list_footer |
Футер с пагинацией и диапазоном записей |
head_css |
Дополнительные стили конкретной страницы |
script |
Дополнительные скрипты конкретной страницы |
detail.html
| Блок | Содержимое |
|---|---|
header |
Заголовок страницы (включает название, хлебные крошки и действия) |
page_title |
Заголовок <h1> внутри шапки страницы |
breadcrumbs |
Хлебные крошки внутри шапки страницы |
modal |
Модальные окна удаления и действия |
content |
Полное содержимое страницы детализации |
detail_before |
Точка вставки перед карточкой детализации |
detail_title |
Область заголовка внутри карточки детализации |
detail_actions |
Кнопки действий внутри карточки детализации |
details_table |
Основная таблица полей и значений |
detail_after |
Точка вставки после карточки детализации |
head_css |
Дополнительные стили конкретной страницы |
script |
Дополнительные скрипты конкретной страницы |
create.html / edit.html
| Блок | Содержимое |
|---|---|
header |
Заголовок страницы (включает название и хлебные крошки) |
page_title |
Заголовок <h1> внутри шапки страницы |
breadcrumbs |
Хлебные крошки внутри шапки страницы |
content |
Полное содержимое страницы формы |
form_before |
Точка вставки перед карточкой формы |
create_card_header / edit_card_header |
Область заголовка внутри карточки формы |
create_form / edit_form |
Группы формы (каждая рендерится через _form_group.html) и их элементы ввода полей |
create_inlines / edit_inlines |
Область inline formset |
form_footer |
Кнопки «Сохранить», «Сохранить и продолжить» и «Добавить еще» |
form_after |
Точка вставки после карточки формы |
head_css |
Дополнительные стили конкретной страницы |
script |
Дополнительные скрипты конкретной страницы |
login.html
| Блок | Содержимое |
|---|---|
header / sidebar |
Оставлены пустыми (страница входа скрывает стандартный каркас приложения) |
content |
Полное содержимое страницы входа |
login_logo |
Логотип, отображаемый над формой входа |
login_title |
Текст заголовка страницы входа |
login_form_before |
Точка вставки перед полями формы |
login_fields |
Поля ввода имени пользователя и пароля |
login_form_footer |
Точка вставки после полей, но внутри формы |
login_card_footer |
Точка вставки, расположенная непосредственно под карточкой входа |
script |
Дополнительные скрипты конкретной страницы |
index.html
| Блок | Содержимое |
|---|---|
head_css |
Дополнительные стили виджетов |
content |
Сетка виджетов дашборда |
script |
Дополнительные скрипты виджетов |
error.html
| Блок | Содержимое |
|---|---|
header / sidebar |
Оставлены пустыми (страница ошибки скрывает стандартный каркас приложения) |
content |
Сообщение об ошибке и связанные действия |
error_actions |
Кнопки действий, отображаемые под сообщением об ошибке (например, кнопка «Назад») |
Tip
Вызывайте {{ super() }} внутри переопределения, чтобы сохранить содержимое встроенного блока и дополнить его, а не заменить. Пример с list_toolbar_extra выше делает именно это, а встроенные index.html и create.html используют тот же прием для блока head_css.
Пример: замена логотипа боковой панели на inline SVG
Передача URL в Admin(logo_url=...) — самый быстрый способ задать логотип, подходящий для большинства случаев, включая внешние .svg-файлы. Однако встроенный шаблон отображает этот URL внутри тега <img>, поэтому SVG не может наследовать CSS-свойства окружающей страницы.
Переопределите блок brand разметкой inline <svg>, если вам нужно, чтобы логотип реагировал на остальной интерфейс.
Реализация
Создайте файл layout.html в вашем каталоге шаблонов. Каждая страница админ-панели наследуется от layout.html, поэтому это одно переопределение действует на всем сайте.
{# my_templates/layout.html #}
{% extends "@starlette-admin/layout.html" %}
{% block brand %}
<svg class="navbar-logo" viewBox="0 0 32 32" fill="currentColor">
<path d="M16 2 L30 9 L30 23 L16 30 L2 23 L2 9 Z" />
</svg>
{% endblock %}
Сохраняйте класс navbar-logo
Оставьте CSS-класс navbar-logo на вашем элементе <svg>. Он обеспечивает вашему inline-графику выравнивание, отступы и размеры framework'а без какого-либо собственного CSS.
Переопределение шаблонов полей
Контекст каждого поля использует три подкаталога:
| Каталог | Где используется |
|---|---|
fields/list/<type>.html |
Ячейка таблицы списка (компактная, только для чтения) |
fields/detail/<type>.html |
Отображение на странице детализации (полное, только для чтения) |
fields/form/<type>.html |
Поле ввода в формах создания и редактирования |
Вы можете переопределить ячейку списка для текстовых полей, не затрагивая форму или отображение детализации:
Чтобы указать одному экземпляру поля использовать ваш шаблон вместо переопределения типа повсюду, задайте атрибут list_template, detail_template, form_template, null_template или empty_template у самого поля:
from starlette_admin.fields import StringField
StringField("status", list_template="fields/list/status_badge.html")
Атрибуты null_template (по умолчанию "fields/detail/_null.html") и empty_template (по умолчанию "fields/detail/_empty.html") являются отдельными слотами. Страницы списка и детализации рендерят их вместо list_template или detail_template всякий раз, когда значение поля равно None либо является пустым списком или кортежем:
Переопределение шаблонов виджетов
Виджеты следуют тому же шаблону переопределения. Поместите свои файлы в каталог widgets/:
Глобальные переменные шаблонов
Эти переменные доступны в каждом шаблоне без явной передачи. Admin регистрирует их как глобальные переменные Jinja2 один раз во время инициализации:
| Переменная | Тип | Описание |
|---|---|---|
views |
list[BaseView] |
Все зарегистрированные представления (используются для рендеринга боковой панели) |
app_title |
str |
Название админ-панели (Admin(title=...)) |
is_auth_enabled |
bool |
True, если настроен провайдер аутентификации |
__name__ |
str |
Префикс имени маршрута админ-панели (например, "admin") |
static_url |
callable |
static_url(request, path, v=None) → URL встроенного статического ресурса. Аргумент v добавляет параметр запроса ?v= для обхода кэша. |
logo_url |
callable |
logo_url(request) → URL логотипа боковой панели или None, если не задан |
login_logo_url |
callable |
login_logo_url(request) → URL логотипа страницы входа или None, если не задан |
favicon_url |
callable |
favicon_url(request) → URL favicon или None, если не задан |
list_url |
callable |
list_url(request, **overrides) → URL с параметрами overrides, объединенными со строкой запроса (используется для ссылок сортировки, пагинации и поиска). Передайте None, чтобы удалить ключ. |
detail_url |
callable |
detail_url(request, key, pk) → URL страницы детализации записи |
edit_url |
callable |
edit_url(request, key, pk) → URL страницы редактирования записи |
export_url |
callable |
export_url(request, key, fmt) → URL скачивания экспорта, несущий состояние фильтра/сортировки/поиска текущей страницы списка |
import_url |
callable |
import_url(request, key) → POST-URL импорта |
get_locale |
callable |
get_locale() → строка активной локали (аргумент request не требуется) |
get_locale_display_name |
callable |
get_locale_display_name(locale) → человекочитаемое имя строки локали |
i18n_config |
I18nConfig |
Объект конфигурации i18n админ-панели |
get_timezone |
callable |
get_timezone() → строка активного часового пояса (аргумент request не требуется) |
get_timezone_display_name |
callable |
get_timezone_display_name(timezone, show_offset=False) → человекочитаемое имя строки часового пояса |
timezone_config |
TimezoneConfig | None |
Конфигурация часового пояса админ-панели |
theme_settings |
TablerSettings |
Конфигурация активной темы Tabler (base, primary, radius, mode), предоставляемая DefaultTheme |
csrf_input |
callable |
csrf_input(request) → рендерит скрытый <input> CSRF-токена |
Note
Функции get_locale, get_locale_display_name, get_timezone и get_timezone_display_name не принимают параметр request. Они читают локаль и часовой пояс из contextvars, которые LocaleMiddleware заполняет на время обработки запроса, а не из объекта Request.
Контекстные переменные отдельных страниц
Помимо перечисленных выше глобальных переменных, каждая страница передает собственный словарь контекста в TemplateResponse.
list.html
| Переменная | Тип | Описание |
|---|---|---|
view |
BaseModelView |
Текущее представление |
title |
str |
Заголовок страницы |
fields |
list[BaseField] |
Текущие видимые столбцы |
all_fields |
list[BaseField] |
Все поля списка (включая скрытые) |
rows |
list[dict] |
Сериализованные данные строк |
total |
int |
Общее количество совпадающих записей для пагинации |
total_pages |
int |
Общее количество страниц |
range_start |
int |
Номер первой записи на этой странице (начиная с 1) |
range_end |
int |
Номер последней записи на этой странице |
list_params |
ListParams |
Разобранное состояние URL (page, page_size, q, sorts, filters) |
filter_logic |
str | None |
Принимает значение "and" или "or" для активной группы фильтров верхнего уровня |
filter_chips |
list |
Дескрипторы активных фильтров (chips) |
filter_builder_fields |
list |
Поля, доступные в интерфейсе конструктора фильтров |
raw_filter |
str | None |
Необработанная строка JSON-фильтра из URL |
_actions |
list |
Доступные массовые действия |
row_actions |
dict[Any, list] |
Доступные действия строк по каждой записи, с ключом по pk |
detail.html
| Переменная | Тип | Описание |
|---|---|---|
view |
BaseModelView |
Текущее представление |
title |
str |
Заголовок страницы |
obj |
dict |
Сериализованная запись |
raw_obj |
Any |
Исходный объект модели до сериализации |
inlines |
list[dict] |
Контекст inline ([{"inline": InlineModelView, "rows": [...]}]) |
_actions |
list |
Доступные действия строк |
create.html / edit.html
| Переменная | Тип | Описание |
|---|---|---|
view |
BaseModelView |
Текущее представление |
title |
str |
Заголовок страницы |
obj |
dict |
Текущие значения полей (значения по умолчанию при создании, существующие значения при редактировании) |
raw_obj |
Any |
Исходный объект модели (только при редактировании, отсутствует при создании) |
errors |
dict[str, list[str]] |
Ошибки валидации, сгруппированные по именам полей (присутствуют только после неудачной отправки) |
inlines |
list[dict] |
Контекст inline formset |
Добавление собственных глобальных переменных и фильтров
Чтобы добавить собственные переменные и функции в шаблоны, создайте подкласс Admin и переопределите __init__. Сначала вызовите super().__init__(), чтобы self.templates существовал до ваших изменений:
from sqlalchemy import create_engine
from starlette_admin.contrib.sqla import Admin
class MyAdmin(Admin):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.templates.env.globals["site_name"] = "My App"
self.templates.env.filters["currency"] = lambda v: f"${v:,.2f}"
engine = create_engine("sqlite:///admin.sqlite")
admin = MyAdmin(engine, title="My Admin")
Встроенные фильтры Jinja2
Каждый экземпляр админ-панели регистрирует эти фильтры во время _setup_templates:
| Фильтр | Сигнатура | Описание |
|---|---|---|
is_custom_view |
view | is_custom_view |
Возвращает True, если ресурс является CustomView |
is_link |
view | is_link |
Возвращает True, если ресурс является Link |
is_model_view |
view | is_model_view |
Возвращает True, если ресурс является BaseModelView |
is_dropdown |
view | is_dropdown |
Возвращает True, если ресурс является DropDown |
tojson |
value | tojson |
Безопасная для HTML сериализация в JSON (заменяет стандартный tojson из Jinja2) |
file_icon |
mime_type | file_icon |
Возвращает полный класс иконки для MIME-типа (например, application/pdf → fa-solid fa-fw fa-file-pdf); переопределите self.templates.env.filters["file_icon"], чтобы использовать свой набор иконок |
to_view |
key | to_view |
Ищет зарегистрированный BaseModelView по строке ключа; выбрасывает HTTPException с кодом 404, если не найден |
is_iter |
value | is_iter |
Возвращает True, если значение является list или tuple |
is_str |
value | is_str |
Возвращает True, если значение является str |
is_dict |
value | is_dict |
Возвращает True, если значение является dict |
ra |
value | ra |
Преобразует строку в член перечисления RequestAction |
safe_url |
url | safe_url |
Возвращает URL, только если он проходит проверку безопасных URL, иначе возвращает "" |
sanitize_html |
html | sanitize_html |
Удаляет запрещенные теги из HTML-строки и возвращает Markup |
Что дальше
- Макеты форм: Разделите формы создания и редактирования на озаглавленные, опционально сворачиваемые группы и переопределите
_form_group.html, чтобы изменить их разметку. - Пользовательские темы: Измените оформление админ-панели, не трогая отдельные шаблоны.
- Пользовательские поля: Свяжите Python-класс поля с его собственным
list_templateилиform_template. - Точки расширения: Полный список подключаемых поверхностей помимо шаблонов.