xladmin backend
xladmin-backend is the backend package published to PyPI as xladmin.
Important:
- package name on PyPI:
xladmin - Python import:
from xladmin import ... - monorepo: Artasov/xladmin
Public API
AdminConfig/ModelConfig/FieldConfigListFilterConfigBulkActionConfig/ObjectActionConfigModelsBlockHttpConfigcreate_router(...)
Compatibility aliases are kept:
Admin*config namescreate_admin_router(...)
Read-only models
Set ModelConfig(..., read_only=True) for records that may only be changed through application domain commands. This is a model-level write restriction, separate from FieldConfig.read_only and from user authentication.
The admin rejects create, PATCH, delete, bulk delete, object actions and bulk actions with HTTP 403 before invoking their write handlers. List/detail and relation reads remain available. Metadata includes read_only, removes writable fields and write actions, and marks fields as read-only even when explicit create/update field lists were configured.
Deleting a writable parent is also rejected if the deletion plan would delete a registered read-only child or clear its foreign key. Delete previews expose those dependencies as protected. Custom handlers registered on other writable models are trusted application code; this setting is not a database-wide authorization mechanism for arbitrary SQL issued by them.
The matching import/export extension rejects both import validation and commit, advertises no import formats, and preserves exports. Consumers must update the backend core and extension together. Frontend consumers should use model metadata to hide write controls; hiding controls alone is not the protection.
Scoped relationship writes
ModelConfig.query_for_list(query, session, user) also defines the available IDs for registered relation targets. Standard create/PATCH validates scalar foreign keys, single relationships and every ID in a relationship collection against this scope, even when an unavailable object is already cached in the session. An unavailable ID produces HTTP 400 without committing the parent edit; duplicate collection IDs are deduplicated in request order. Models without a configured scope retain their ordinary relation selection semantics.
Internal mutation helpers now require the runtime registry and authenticated user as keyword arguments. Upgrade the matching import/export extension together: its validation and commit pass the same context, and imported existing targets must also be visible. Custom setters/handlers remain trusted application code. This is assignment validation, not automatic database routing or row-level security for arbitrary SQL.
Minimal Example
from xladmin import AdminConfig, HttpConfig, ModelConfig, create_router
from src.core.auth.dependencies import get_current_user
from src.core.db.session import get_db_session
from src.modules.identity.models import UserORM
config = AdminConfig(
models=(
ModelConfig(model=UserORM),
),
)
router = create_router(
HttpConfig(
registry=config,
get_db_session_dependency=get_db_session,
get_current_user_dependency=get_current_user,
is_allowed=lambda user: bool(user.is_staff),
),
)
ModelConfig(model=UserORM) is enough for a basic admin. The library derives default slug, title, search fields, and ordering from the ORM model.
Features
- list / detail / create / patch / delete endpoints
- current-user endpoint for frontend sidebar identity:
GET /xladmin/me/ - logout endpoint for frontend logout buttons:
POST /xladmin/logout/ - bulk actions and object actions
- relation choices and relation filters
- single and multi-select relation filters via
ListFilterConfig(..., multiple=True, input_kind="relation-multiple") - overview metadata and model blocks
query_for_listand customsearch_query_builder- mode-specific form fields with
hidden_in_create/hidden_in_update - custom create defaults with
create_item_factory - delete preview for single and bulk delete
- RU / EN locale metadata for the frontend
Current User And Logout
The frontend Shell can show the current user in the sidebar and call logout from the sidebar action.
The backend router exposes two endpoints for this:
GET /xladmin/me/POST /xladmin/logout/
/xladmin/me/ uses get_current_user_dependency and returns a small payload:
{
"id": 1,
"login": "admin@example.com",
"email": "admin@example.com",
"name": "Admin"
}
The login value is resolved from the first available user attribute in this order:
username, email, login, name, then id.
By default /xladmin/logout/ only checks that the user can access admin and returns 204.
Real applications should pass logout_dependency to HttpConfig to clear cookies, sessions, tokens, or any other auth state.
Headers and cookies set by logout_dependency are preserved in the final 204 response.
from fastapi import Response
from xladmin import HttpConfig, create_router
async def logout_admin_user(response: Response) -> None:
response.delete_cookie("session")
router = create_router(
HttpConfig(
registry=admin_config,
get_db_session_dependency=get_db_session,
get_current_user_dependency=get_current_user,
is_allowed=lambda user: bool(user.is_staff),
logout_dependency=logout_admin_user,
),
)
If your auth system needs the current user in the logout handler, add it as a regular FastAPI dependency inside your function.
Multi-Select Relation Filters
If one relation filter is not enough, you can expose a multi-select variant that works as an autocomplete with add/remove chips on the frontend.
from xladmin import ListFilterConfig
ListFilterConfig(
slug="role_ids",
label="Roles",
field_name="roles",
input_kind="relation-multiple",
multiple=True,
relation_model=RoleORM,
relation_label_field="name",
)
Create And Update Fields
If a field should be visible only in one form mode, use hidden_in_create or hidden_in_update.
from xladmin import FieldConfig, ModelConfig
ModelConfig(
model=UserORM,
fields={
"password": FieldConfig(
input_kind="password",
hidden_in_update=True,
value_setter=set_user_password,
),
"new_password": FieldConfig(
input_kind="password",
hidden_in_create=True,
value_getter=lambda _user: "",
value_setter=set_user_password,
),
},
)
If create requires hidden service fields, use create_item_factory.
from xladmin import ModelConfig
def create_admin_user(payload, session, user):
del payload, session, user
return UserORM(
date_joined=AuthBase.now(),
secret_key=AuthBase.generate_secret_key(),
)
ModelConfig(
model=UserORM,
create_fields=("username", "email", "password"),
create_item_factory=create_admin_user,
)
If create needs a fully custom form and payload handler, use create_form + create_handler.
The same FormFieldConfig mechanism is also available for object_actions and bulk_actions via form=....
If an action has no form, the frontend runs it immediately as before.
For datetime inputs the built-in dialog uses the MUI action bar with a localized Today / Сегодня button, which inserts the current date and time.
from xladmin import FormFieldConfig, FormFieldOptionConfig, ModelConfig
async def create_proxy(session, model_config, payload, user):
del session, model_config, user
parsed = ProxyBase.parse_raw(f"{payload['scheme']}://{payload['proxy']}")
return ProxyORM(
name=parsed.name,
scheme=parsed.scheme,
host=parsed.host,
port=parsed.port,
username=parsed.username,
password=parsed.password,
created_at=ProxyBase.now(),
updated_at=ProxyBase.now(),
)
ModelConfig(
model=ProxyORM,
create_form=(
FormFieldConfig(
name="scheme",
label="Scheme",
input_kind="select",
required=True,
options=(
FormFieldOptionConfig(value="http", label="HTTP"),
FormFieldOptionConfig(value="socks5h", label="SOCKS5H"),
),
),
FormFieldConfig(
name="proxy",
label="Proxy",
placeholder="login:password@ip:port",
required=True,
),
),
create_handler=create_proxy,
)
Compatibility
FastAPI >=0.115,<1.0Pydantic >=2.9,<3.0SQLAlchemy >=2.0,<3.0Python >=3.12
Development
uv sync --extra dev
uv run pytest
uv run ruff check .
uv run mypy
uv run python -m build
uv run python -m twine check dist/*
Docs
Release files for xladmin 0.10.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xladmin-0.10.0.tar.gz | 40.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xladmin-0.10.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 73.6 kB
Release files / xladmin-0.10.0.tar.gz
| Download URL | xladmin-0.10.0.tar.gz |
|---|---|
| Size | 40.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5e6b5d744f042e4f6f653792a3288f6f82b0db8443a5ffb3065b5d0b7375ae60
|
|
BLAKE2b-256 checksum How to use checksums |
c0d4b5317fc35f64604e319d075e79454a8a49d79c1beac45a5a2372ba9ad9e6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / xladmin-0.10.0-py3-none-any.whl
| Download URL | xladmin-0.10.0-py3-none-any.whl |
|---|---|
| Size | 33.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ccb77e8054ad0848573d8c2adda04c46987795bbac7eeab5734eb4fd9b152c18
|
|
BLAKE2b-256 checksum How to use checksums |
b7894c7c3da004e02097021475904c3832b0380f84bdbba0985b913dd8defc2c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|