Traducción automática supervisada
Este contenido se traduce mediante generación automática guiada por glosarios y guías de estilo revisados por personas. Dado que el texto no se revisa manualmente línea por línea, pueden producirse errores ocasionales o expresiones poco naturales.
En caso de cualquier discrepancia, la versión en inglés constituye la autoridad y la fuente de referencia.
Plugins
Un plugin es un paquete de Python que extiende starlette-admin mediante un único argumento de constructor. Un plugin puede agrupar cualquier combinación de campos, plantillas, recursos estáticos, convertidores de modelos, filtros, formatos de importación/exportación, backends de almacenamiento, suscriptores de eventos, vistas, rutas, middlewares, recursos de tema y catálogos de traducción.
Uso de un plugin
Pase los plugins a través del argumento plugins cuando construya su instancia de Admin:
from starlette_admin_geospatial import GeospatialPlugin
from starlette_admin.contrib.sqla import Admin
admin = Admin(engine, plugins=[GeospatialPlugin(default_zoom=13)])
El constructor del plugin recibe las opciones, y la lista se pasa directamente a Admin. No hay nada más que configurar ni registrar. Las opciones fluyen desde el constructor hasta el backend de Python, las plantillas Jinja y el JavaScript del frontend.
Creación de un plugin
Para escribir un plugin, parta de la plantilla oficial de cookiecutter. Esta genera un paquete publicable con la estructura de directorios y la configuración adecuadas.
Requisitos previos
Instale cookiecutter con su gestor de paquetes. Consulte la guía oficial de instalación para conocer los detalles:
Scaffolding
Ejecute la plantilla de cookiecutter desde cualquier ubicación:
La plantilla le pedirá el nombre del plugin, el slug del paquete, la versión y algunas otras variables. Cuando termine, obtendrá un paquete autocontenido con:
- Un directorio
src/que contiene la clase de su plugin y los campos. - Carpetas
templates/,static/ytranslations/correctamente organizadas en namespaces. - Una suite de pruebas completa.
- Una aplicación de ejemplo ejecutable.
La API de plugins
En el núcleo de cada plugin hay una subclase de BasePlugin (starlette_admin.plugins.BasePlugin), que le proporciona hooks para registrar sus funcionalidades mientras Admin se inicializa.
El atributo name es un identificador único en kebab-case que sirve además como namespace para sus plantillas y recursos estáticos. Cada plantilla y archivo estático que incluya su plugin debe encontrarse bajo plugins/<name>/.
Carpetas de assets
Un plugin puede contener exactamente tres carpetas en la raíz de su paquete. No hay nada que registrar, porque el admin las encuentra por convención:
templates/: plantillas Jinja, que deben situarse bajotemplates/plugins/<name>/.static/: recursos estáticos como archivos CSS y JS, que deben situarse bajostatic/plugins/<name>/.translations/: catálogos de traducción de Babel.
Mantenerse dentro del namespace plugins/<name>/ evita que sus assets colisionen con los archivos principales o con otros plugins, dejándolos además sobrescribibles mediante el propio templates_dir o static_dir del usuario.
Hooks declarativos
Sobrescriba los hooks declarativos para inyectar assets, registrar vistas o montar rutas.
css_links(self, request: Request) -> Sequence[str]: añade hojas de estilo al layout de todas las páginas del admin.js_links(self, request: Request) -> Sequence[str]: añade scripts al layout de todas las páginas del admin.views(self) -> Sequence[BaseView]: devuelve las vistas que se registrarán en la barra lateral del admin. Devuelva unDropDownpara agruparlas.routes(self) -> Sequence[Route | Mount]: devuelve endpoints sin interfaz montados bajo/plugins/<name>/, lo cual resulta práctico para webhooks y endpoints proxy.middlewares(self) -> Sequence[Middleware]: añade middlewares de Starlette.template_globals(self) -> dict[str, Any]: expone globals de Jinja, con el prefijo<name>_para evitar colisiones.template_filters(self) -> dict[str, Callable]: expone filtros de Jinja, con el prefijo<name>_de la misma manera.
El hook de setup
setup(self, admin: BaseAdmin) -> None integra su plugin con los registros principales. Úselo para registrar convertidores de modelos, filtros, formatos de importación y exportación, backends de almacenamiento y suscriptores de eventos. Se ejecuta después de aplicar los hooks declarativos.
El hook de ciclo de vida
on_mount(self, admin: BaseAdmin) -> None se ejecuta exactamente una vez, después de que la sub-aplicación de Starlette se haya construido y montado. La aplicación construida está disponible como admin.app.
Plantillas y overrides
Las plantillas de los plugins se incorporan automáticamente a la cadena de loaders. Un usuario puede sobrescribir una colocando un archivo en la ruta correspondiente dentro de su propio templates_dir, que siempre tiene prioridad. Para sobrescribir plugins/geospatial/fields/form/point.html, por ejemplo, debe crear templates_dir/plugins/geospatial/fields/form/point.html.
Para que un override del usuario pueda extender el original de forma segura, cada plugin dispone de un prefijo de mapeo @<name> que funciona igual que el prefijo @core. El override comienza con {% extends "@geospatial/fields/form/point.html" %} y extiende la plantilla base del plugin sin incluirse a sí mismo recursivamente.
Integración con el JavaScript del frontend
Un plugin que incluya campos personalizados debe empaquetar sus scripts del frontend conforme al contrato de inicializadores de campos. Así se garantiza que funcionen tanto en cargas completas de página como en fragmentos insertados dinámicamente.
- Apunte localmente: realice las consultas dentro del elemento
containerque recibe, nunca sobre eldocumentglobal. - Sea idempotente: el núcleo ejecuta el inicializador cuando el DOM está listo y de nuevo cada vez que inserta filas inline o fragmentos.
- Use atributos data: lea la configuración desde los atributos
data-*renderizados en el elemento del campo.
(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);
});
})();
Puntos de extensión mediante el hook de setup
Los plugins utilizan los registros públicos existentes en lugar de una vía de extensión propia separada.
- Converters: llame a
register_converter, desde el backend contrib al que apunte, para mapear tipos de columnas ORM a sus clases de campos. Defina el campo en sí como una subclase ordinaria deStringField, almacenando y mostrando las geometrías como texto 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)
- Filters: llame a
register_filterspara asociar clases de filtros a un tipo de campo.
from starlette_admin.contrib.sqla.filters import register_filters
register_filters(MyGeoField, WithinBoundingBoxFilter)
- Storage: llame a
register_storagepara exponer un nuevo backend, como Azure o GCS.
- Importers and Exporters: use
register_import_formatyregister_export_format.
from starlette_admin.export import register_export_format
register_export_format("pdf", PDFExporter())
Un plugin puede dar soporte a varios backends ORM, así que impórtelos condicionalmente dentro de setup(). De este modo, el plugin sigue cargándose aunque el usuario solo haya instalado uno de ellos:
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
Próximos pasos
- Temas personalizados: empaquete y comparta un sistema visual completo, usando el mismo flujo de trabajo de cookiecutter.
- Eventos: la API de suscriptores que un plugin registra desde su hook
setup(). - Puntos de extensión: todos los registros y clases base en los que un plugin puede integrarse.