Перейти к содержанию
Машинный перевод под контролем человека

Этот контент переведён с помощью машинной генерации, направляемой составленными людьми глоссариями и руководствами по стилю. Поскольку текст не проверяется вручную построчно, возможны отдельные ошибки или неестественные формулировки.

В случае любых расхождений авторитетным источником считается оригинальная версия на английском языке.

Читать оригинал на английском

Шаблоны

Каждая страница административной панели — это шаблон 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   ← затмевает встроенный list.html
{# 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 Поле ввода в формах создания и редактирования

Вы можете переопределить ячейку списка для текстовых полей, не затрагивая форму или отображение детализации:

my_templates/
└── fields/
    └── list/
        └── text.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 либо является пустым списком или кортежем:

StringField("status", null_template="fields/detail/_status_null.html")

Переопределение шаблонов виджетов

Виджеты следуют тому же шаблону переопределения. Поместите свои файлы в каталог widgets/:

my_templates/
└── widgets/
    └── stat_widget.html

Глобальные переменные шаблонов

Эти переменные доступны в каждом шаблоне без явной передачи. 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/pdffa-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.
  • Точки расширения: Полный список подключаемых поверхностей помимо шаблонов.