Машинный перевод под контролем человека
Этот контент переведён с помощью машинной генерации, направляемой составленными людьми глоссариями и руководствами по стилю. Поскольку текст не проверяется вручную построчно, возможны отдельные ошибки или неестественные формулировки.
В случае любых расхождений авторитетным источником считается оригинальная версия на английском языке.
Плагины
Плагин — это Python-пакет, который расширяет starlette-admin через один аргумент конструктора. Плагин может объединять в себе любые комбинации полей, шаблонов, статических ресурсов, конвертеров моделей, фильтров, форматов импорта/экспорта, storage backend'ов, подписчиков событий, представлений, маршрутов, middleware, ресурсов темы и каталогов переводов.
Использование плагина
Передавайте плагины через аргумент plugins при создании экземпляра Admin:
from starlette_admin_geospatial import GeospatialPlugin
from starlette_admin.contrib.sqla import Admin
admin = Admin(engine, plugins=[GeospatialPlugin(default_zoom=13)])
Конструктор плагина принимает опции, а список передаётся напрямую в Admin. Больше ничего настраивать или регистрировать не нужно. Опции передаются от конструктора вниз по цепочке: в Python backend, шаблоны Jinja и frontend JavaScript.
Создание плагина
Чтобы написать плагин, начните с официального cookiecutter-шаблона. Он генерирует готовый к публикации пакет с правильной структурой каталогов и конфигурацией.
Предварительные требования
Установите cookiecutter с помощью вашего пакетного менеджера. Подробности см. в официальном руководстве по установке:
Скаффолдинг
Запустите cookiecutter-шаблон из любого каталога:
Шаблон запросит у вас имя плагина, slug пакета, версию и несколько других переменных. По завершении вы получите самодостаточный пакет со следующим содержимым:
- Каталог
src/, в котором находятся класс вашего плагина и поля. - Правильно именованные каталоги
templates/,static/иtranslations/. - Полный набор тестов.
- Запускаемое примерное приложение.
API плагина
В основе каждого плагина лежит подкласс BasePlugin (starlette_admin.plugins.BasePlugin), который предоставляет hook'и для регистрации ваших функций во время инициализации Admin.
Атрибут name — это уникальный идентификатор в kebab-case, который одновременно служит пространством имён для ваших шаблонов и статических ресурсов. Каждый шаблон и каждый статический файл, поставляемые вашим плагином, должны находиться внутри plugins/<name>/.
Каталоги ресурсов
Плагин может содержать ровно три каталога в корне своего пакета. Регистрировать их не нужно, поскольку admin находит их по соглашению:
templates/: шаблоны Jinja, которые должны располагаться внутриtemplates/plugins/<name>/.static/: статические ресурсы, такие как CSS- и JS-файлы, которые должны располагаться внутриstatic/plugins/<name>/.translations/: каталоги переводов Babel.
Соблюдение пространства имён plugins/<name>/ защищает ваши ресурсы от конфликтов с файлами ядра или другими плагинами, оставляя их переопределяемыми через собственные templates_dir или static_dir пользователя.
Декларативные hook'и
Переопределяйте декларативные hook'и, чтобы добавлять ресурсы, регистрировать представления или монтировать маршруты.
css_links(self, request: Request) -> Sequence[str]: добавляет таблицы стилей в макет каждой страницы admin.js_links(self, request: Request) -> Sequence[str]: добавляет скрипты в макет каждой страницы admin.views(self) -> Sequence[BaseView]: возвращает представления для регистрации в боковой панели admin. ВернитеDropDown, чтобы сгруппировать их.routes(self) -> Sequence[Route | Mount]: возвращает endpoint'ы без интерфейса, монтируемые по пути/plugins/<name>/— это удобно для webhook'ов и proxy endpoint'ов.middlewares(self) -> Sequence[Middleware]: добавляет Starlette middlewares.template_globals(self) -> dict[str, Any]: предоставляет глобальные переменные Jinja с префиксом<name>_, чтобы исключить коллизии.template_filters(self) -> dict[str, Callable]: предоставляет фильтры Jinja с таким же префиксом<name>_.
Hook setup
setup(self, admin: BaseAdmin) -> None интегрирует ваш плагин с основными реестрами ядра. Используйте его для регистрации конвертеров моделей, фильтров, форматов импорта и экспорта, storage backend'ов и подписчиков событий. Он выполняется после применения декларативных hook'ов.
Lifecycle hook
on_mount(self, admin: BaseAdmin) -> None выполняется ровно один раз, после того как Starlette sub-application собран и смонтирован. Собранное приложение доступно как admin.app.
Шаблоны и переопределения
Шаблоны плагина автоматически включаются в цепочку загрузчиков. Пользователь может переопределить шаблон, разместив файл по соответствующему пути внутри собственного templates_dir, который всегда имеет приоритет. Например, чтобы переопределить plugins/geospatial/fields/form/point.html, пользователь создаёт файл templates_dir/plugins/geospatial/fields/form/point.html.
Чтобы пользовательское переопределение могло безопасно расширять оригинал, каждому плагину назначается префикс @<name>, работающий аналогично префиксу @core. Переопределение начинается с {% extends "@geospatial/fields/form/point.html" %} и расширяет базовый шаблон плагина без рекурсивного включения самого себя.
Интеграция frontend JavaScript
Плагин, поставляющий кастомные поля, должен упаковывать свои frontend-скрипты в соответствии с контрактом инициализатора полей. Это гарантирует их работу как при полной загрузке страницы, так и при динамической вставке фрагментов.
- Работайте локально: выполняйте поиск внутри переданного вам элемента
container, а не глобальногоdocument. - Будьте идемпотентны: ядро запускает инициализатор при готовности DOM и повторно всякий раз, когда вставляет inline-строки или фрагменты.
- Используйте data-атрибуты: читайте конфигурацию из атрибутов
data-*, отрендеренных на элементе поля.
(function () {
function initSlider(container) {
var input = container.querySelector('input[type="range"]');
var output = container.querySelector(".sa-slider-output");
var suffix = container.dataset.suffix || "";
input.addEventListener("input", function () {
output.textContent = input.value + suffix;
});
}
// Register the initializer so core runs it on the right lifecycle events
window.StarletteAdmin.registerFieldInitializer(function (element) {
element.querySelectorAll("[data-sa-slider]").forEach(initSlider);
});
})();
Точки расширения через hook setup
Плагины используют существующие публичные реестры, а не отдельный собственный механизм расширений.
- Конвертеры: вызывайте
register_converterиз того contrib backend'а, который вы поддерживаете, чтобы сопоставить типы колонок ORM с вашими классами полей. Само поле определите как обычный подклассStringField, хранящий и отображающий геометрии в виде WKT-текста:
from dataclasses import dataclass
from typing import Any
from starlette_admin.contrib.sqla.converters import register_converter
from starlette_admin.fields import StringField
@dataclass
class MyGeoField(StringField):
...
@register_converter("Geometry")
def convert_geometry(*args: Any, **kwargs: Any) -> MyGeoField:
return MyGeoField(*args, **kwargs)
- Фильтры: вызывайте
register_filters, чтобы привязать классы фильтров к типу поля.
from starlette_admin.contrib.sqla.filters import register_filters
register_filters(MyGeoField, WithinBoundingBoxFilter)
- Storage: вызывайте
register_storage, чтобы предоставить новый backend, например Azure или GCS.
- Импортёры и экспортёры: используйте
register_import_formatиregister_export_format.
from starlette_admin.export import register_export_format
register_export_format("pdf", PDFExporter())
Плагин может поддерживать несколько ORM backend'ов, поэтому импортируйте их условно внутри setup(). Благодаря этому плагин продолжит загружаться, даже если пользователь установил только один из них:
def setup(self, admin: "BaseAdmin") -> None:
try:
from starlette_admin_geospatial.contrib.sqla import register_sqla_converters
register_sqla_converters()
except ImportError:
pass # geoalchemy2 or sqlalchemy not installed
Что дальше
- Кастомные темы: упаковывайте и делитесь полноценными визуальными системами, используя тот же workflow на основе cookiecutter.
- События: subscriber API, который плагин регистрирует из своего hook'а
setup(). - Точки расширения: все реестры и базовые классы, которые может использовать плагин.