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.
Múltiples instancias de Admin
Cada instancia de Admin que construya es una sub-aplicación Starlette autocontenida. Monte tantas como necesite, cada una con su propio base_url, route_name, proveedor de autenticación y vistas.
from sqlalchemy import create_engine
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.sessions import SessionMiddleware
from starlette.requests import Request
from starlette_admin.auth import AdminUser, AuthProvider, LoginFailed
from starlette_admin.contrib.sqla import Admin, ModelView
from myapp.models import Order, Post, User
engine = create_engine("sqlite:///app.sqlite")
app = Starlette()
STAFF = {"staff": "staffpass"}
SUPERADMINS = {"root": "rootpass"}
class StaffAuth(AuthProvider):
async def login(
self, username: str, password: str, remember_me: bool, request: Request
) -> None:
if STAFF.get(username) == password:
request.session["user"] = username
return
raise LoginFailed("Invalid username or password")
async def authenticate(self, request: Request) -> AdminUser | None:
username = request.session.get("user")
return AdminUser(username=username) if username in STAFF else None
async def logout(self, request: Request) -> None:
request.session.clear()
class SuperAdminAuth(AuthProvider):
async def login(
self, username: str, password: str, remember_me: bool, request: Request
) -> None:
if SUPERADMINS.get(username) == password:
request.session["user"] = username
return
raise LoginFailed("Invalid username or password")
async def authenticate(self, request: Request) -> AdminUser | None:
username = request.session.get("user")
return AdminUser(username=username) if username in SUPERADMINS else None
async def logout(self, request: Request) -> None:
request.session.clear()
staff_admin = Admin(
engine,
title="Staff Admin",
base_url="/staff",
route_name="staff_admin",
auth_provider=StaffAuth(),
secret_key="staff-secret-change-me",
middlewares=[Middleware(SessionMiddleware, secret_key="staff-secret-change-me")],
)
staff_admin.add_view(ModelView(Order))
staff_admin.add_view(ModelView(Post))
staff_admin.mount_to(app)
root_admin = Admin(
engine,
title="Super Admin",
base_url="/root",
route_name="root_admin",
auth_provider=SuperAdminAuth(),
secret_key="root-secret-change-me",
middlewares=[Middleware(SessionMiddleware, secret_key="root-secret-change-me")],
)
root_admin.add_view(ModelView(Order))
root_admin.add_view(ModelView(User))
root_admin.mount_to(app)
En este ejemplo, /staff muestra una página de inicio de sesión respaldada por StaffAuth y /root muestra otra independiente respaldada por SuperAdminAuth. Iniciar sesión en uno no otorga acceso al otro: cada SessionMiddleware firma su cookie con su propio secret_key, por lo que cada instancia de Admin solo lee los datos de sesión que escribió su propio proveedor de autenticación.
base_url y route_name
base_url y route_name son parámetros del constructor de las clases Admin y BaseAdmin en starlette_admin/base.py. Sus valores predeterminados son /admin y "admin":
def __init__(
self,
title: str = "Admin",
base_url: str = "/admin",
route_name: str = "admin",
...
)
base_urlestablece el prefijo de ruta donde se monta el admin. Se pasa directamente a la llamada internaapp.mount(self.base_url, app=admin_app, name=self.route_name), por lo que debe ser único por instancia. De lo contrario, un montaje ocultaría al otro.route_namees el nombre bajo el cual Starlette registra el montaje. Cada URL que genera el admin, ya sea para listas, detalles, ediciones, exportaciones o recursos estáticos, proviene derequest.url_for(route_name + ":list", ...), y cada plantilla de página leerequest.app.state.ROUTE_NAMEpara obtener el prefijo correcto al construir enlaces.
mount_to construye una sub-aplicación Starlette nueva para cada instancia de admin, de modo que el middleware, las rutas y las variables globales de plantilla permanecen aislados. Admin no es un singleton a nivel de proceso: construya tantas instancias independientes como necesite su aplicación.
Warning
Asigne a cada Admin un route_name distinto. El router de Starlette resuelve url_for("admin:list", ...) haciendo coincidir el nombre del montaje, de modo que dos admins que compartan un route_name dejan la aplicación padre con dos montajes bajo el mismo nombre, y url_for se resuelve con el primero que Starlette encuentre. Todos los enlaces internos del segundo admin, incluidos los enlaces de edición, los recursos estáticos y los endpoints de exportación, apuntarán entonces silenciosamente al base_url del primer admin.
Compartir vistas frente a definir vistas separadas
add_view recibe una instancia de vista y la modifica durante la configuración. Para una BaseModelView, esa configuración vincula callbacks internos al admin con el que se registra, incluida la forma en que los campos HasOne y HasMany resuelven los enlaces a registros relacionados.
Si registra la misma instancia de vista en dos admins, la segunda llamada a add_view sobrescribe esos callbacks, por lo que los enlaces de relación en las páginas del primer admin se resolverán contra las vistas y URLs del segundo admin.
Para evitarlo, asigne a cada admin una instancia nueva de la clase ModelView. La clase no contiene estado específico del admin; solo lo contienen las instancias:
staff_admin.add_view(ModelView(Order))
root_admin.add_view(ModelView(Order)) # separate instance of the same class; this is safe
Cuando los dos admins necesitan comportamientos diferentes, como reglas de visibilidad distintas o permisos de can_delete, escriba una subclase para cada uno en lugar de modificar una instancia compartida en tiempo de ejecución:
class StaffOrderView(ModelView):
fields_default_sort = ["-created_at"]
def can_delete(self, request: Request) -> bool:
return False
class RootOrderView(ModelView):
fields_default_sort = ["-created_at"]
staff_admin.add_view(StaffOrderView(Order))
root_admin.add_view(RootOrderView(Order))
¿Qué sigue?
- Autenticación: El contrato completo de
AuthProvideryOAuthProvider. - Puntos de extensión: Todas las demás superficies conectables disponibles en la clase
Admin. - Inicio rápido: La configuración fundamental de un solo admin sobre la cual se basa esta guía.