Таблицы и поля
- Настройки Admin
- Регистрация таблиц
- Настройки полей
- Собственные контролы полей
- Значения по умолчанию, NULL и типы
- Несколько источников данных
- Конкурирующее редактирование
- Контролы типов БД
- Группы таблиц
- Редактирование ячеек списка
- Большие таблицы и время запросов
- Общие лимиты чтения
- Поля только для ввода в форме
Настройки Admin
Все аргументы Admin(...) передаются по имени. Настройте экземпляр до обработки запросов.
| Аргумент | Значение по умолчанию и назначение |
|---|---|
auth, secret_key |
Обязательные backend авторизации и постоянный случайный секрет не короче 32 байт; см. авторизацию |
adapters |
(); адаптеры существующих данных с включённым автообнаружением |
title, description |
"Quite Admin", ""; название приложения и необязательное описание |
language |
"en"; "en" или "ru" для встроенного интерфейса |
theme, logo |
None; стандартные Theme() и логотип либо своя палитра и путь к локальному изображению; см. оформление |
templates_dir |
None; каталог шаблонов приложения с приоритетом над встроенными |
secure_cookies |
True; False допустим только для локального HTTP |
session_cookie |
"quite_admin_session"; разным админкам одного домена задайте разные имена |
session_max_age |
28800 секунд (8 часов); положительное время жизни сессии, ревизий и курсоров |
logger, log_structured |
None, False; логирование под управлением приложения |
hooks |
None; необязательные OperationHooks для жизненного цикла записи |
storage |
None; URL окружения или локальная SQLite либо явно заданный путь, URL или AdminStore; см. хранилище |
history_enabled |
True; запись и отображение истории действий |
history_level |
"changes"; завершённые изменения; "audit" добавляет авторизацию, экспорт и ошибки, "all" — чтения |
history_retention_days |
90; срок хранения в днях, None отключает очистку; подробности |
history_permission |
None; необязательный sync/async callback доступа к истории и поиску пользователей |
query_timeout |
None; положительный лимит секунд на чтение админки, включая очередь и ожидание async-пула |
max_concurrent_reads |
None; положительное целое ограничение одновременных чтений этой админки в одном процессе |
schema_auto_limit |
True; большие схемы БД открываются без выбранных таблиц; начальный выбор |
schema_table_limit |
100; положительный целочисленный порог доступных зарегистрированных таблиц для начального выбора схемы |
add_adapter(adapter, discover=True, namespace=None) подключает источник данных,
register(model, adapter=..., options=...) выбирает и настраивает модель,
configure(slug, options=...) настраивает обнаруженную таблицу. ModelOptions заменяет
настройки таблицы целиком: указывайте все значения, которые хотите сохранить.
Подключайте через mount_fastapi(app, "/admin"), mount_litestar(app, "/admin")
или litestar_route("/admin"); см. интеграцию.
Регистрация таблиц
Автообнаружение можно отключить и зарегистрировать только нужные модели:
from quite_admin import Admin, FieldOptions, ModelOptions
admin = Admin(auth=auth_backend, secret_key=session_secret)
admin.add_adapter(adapter, discover=False)
admin.register(User, options=ModelOptions(
label="Пользователи",
list_fields=("id", "email", "is_active"),
edit_fields=("email", "is_active", "created_at"),
create_fields=("email", "is_active"),
readonly_fields=("created_at",),
exclude=("password_hash",),
search_fields=("email",),
filter_fields=("is_active", "created_at"),
fields={"email": FieldOptions(label="Email", help_text="Адрес для уведомлений")},
can_create=False,
can_delete=False,
))
ModelOptions работает со всеми адаптерами.
У существующей записи одна страница просмотра и редактирования. Без права изменения поля
доступны только для чтения, а прямой POST отклоняется сервером. edit_fields определяет
порядок и набор изменяемых полей; остальные видимые поля остаются на странице только для чтения.
Нажатие на неинтерактивную часть строки открывает запись. Ссылка поддерживает клавиатуру
и открытие в новой вкладке. Карандаши, переключатели, флажки выбора и ссылки на связанные
записи или другие страницы выполняют своё действие. Удаление ведёт на подтверждение.
Возврат из записи сохраняет исходный список; см. обзор интерфейса.
Без права просмотра/изменения ссылки на запись нет; удаление зависит от соответствующего права.
При включённом автообнаружении register() переопределяет настройки модели.
Для reflection используйте admin.configure("table_name", options=...) без импорта модели.
Настройки полей
| Параметр | Поведение |
|---|---|
slug, label |
Имя таблицы в URL и интерфейсе; по умолчанию берутся из схемы адаптера |
list_fields |
Столбцы списка; по умолчанию первые шесть видимых полей |
edit_fields |
Порядок и набор изменяемых полей существующей записи |
create_fields |
Порядок и набор полей создания |
readonly_fields |
Показывает значения, запрещая запись |
exclude |
Скрывает поля и запрещает запись и поиск по ним |
search_fields |
Строковые поля текстового поиска |
filter_fields |
Видимые скалярные фильтры; None обнаруживает поддерживаемые поля, () отключает их |
relations, readonly_relations |
Доступные редакторы связей и связи только для чтения |
record_label |
Поле подписи записи в выборе связей |
fields |
Подписи, подсказки и read-only настройки отдельных полей |
can_create, can_edit, can_delete |
Разрешает или запрещает операции |
permission |
Sync/async callback (request, identity, action); см. права |
expose_sensitive_fields |
Явные исключения из автоматического скрытия секретных имён; exclude имеет приоритет |
writable_protected_fields |
Явные исключения из защиты служебных полей и привилегий |
group |
Необязательное непустое название группы каталога на главной, поиска таблиц и схемы БД |
list_editable |
None включает доступные столбцы, кортеж выбирает поля, () отключает inline-правки |
pagination |
"offset" для точного количества и номеров страниц либо "cursor" для обхода с сортировкой без COUNT |
input_fields |
Кортеж Field только для форм, значения которых обрабатывает handler/сервис |
create_schema, update_schema |
Необязательные модели Pydantic 2 для проверки переданных значений |
create_handler, update_handler |
Сервисы модели вместо нативного создания/редактирования; см. сервисы и схемы |
actions |
Кортеж кнопок RecordAction с проверкой прав на странице записи |
bulk_actions |
(); команды RecordAction для конкретной таблицы, доступные только в меню иконки справа в списке; массовые действия |
bulk_delete |
True; массовое удаление на странице с обычной проверкой delete; массовые действия |
FieldOptions(read_only=True) тоже запрещает изменение поля.
Опечатка в настройке имени поля вызывает ошибку конфигурации.
По умолчанию скрываются имена с password, имена secret, token, credentials,
authorization, api_key, private_key, session_key, а также окончания _token
и _secret. exclude всегда имеет приоритет. Эвристика не находит все секреты —
проверяйте обнаруженную схему. Для намеренного исключения используйте
expose_sensitive_fields=("api_key",).
Поля created_at, updated_at, deleted_at, created_by, updated_by, is_admin,
is_superuser, is_staff, roles, permissions по умолчанию только для чтения.
Явно разрешить изменение можно через writable_protected_fields=("is_admin",).
Это не отменяет ограничения readonly_fields, generated и read-only метаданные адаптера.
Скрытие PK не делает идентификатор секретным: ключ закодирован в URL записи.
Собственные контролы полей
FieldOptions(display="url"), display="json" и display="multiline" добавляют встроенные контролы значений.
url_template="https://short.example/{value}" собирает ссылку из скалярного значения
только для отображения, без formatter. Подробности: безопасные URL,
копирование, read-only и сохранение прежних правил записи/экспорта.
FieldOptions принимает kind, parser, formatter, template, подпись и подсказку.
Пример счётчика #7:
FieldOptions(
kind="string",
parser=lambda raw: int(raw.removeprefix("#")),
formatter=lambda value: "#" + str(value),
template="counter.html",
)
Parser возвращает нативное значение или выбрасывает ValueError. Formatter возвращает
текст, который экранируется шаблонами. Шаблон получает field, values, errors
и общий контекст. Контрол должен иметь имя {{ field.name }} и значение
values[field.name]. Он включается только для изменяемого поля; серверный allowlist,
подпись и отображение ошибок сохраняются. Неизвестные типы адаптера остаются read-only,
пока свой адаптер явно не опишет поддерживаемый тип/codec. См. расширения.
Первичный ключ нельзя менять после создания. Generated и computed поля не редактируются.
Binary и неизвестные типы показываются только для чтения. Неавтоматический первичный
ключ можно задать при создании записи.
Неизвестные, скрытые и read-only поля в POST отклоняются с 422.
Сервер проверяет allowlist независимо от браузерной формы.
Значения по умолчанию, NULL и типы
Для поля с default форма создания предлагает Использовать значение по умолчанию модели / базы данных.
Снимите отметку, чтобы передать своё значение.
Пустое nullable-поле означает NULL; пустая строка NOT NULL означает "".
Поддержаны строки, целые, bool, float, Decimal, UUID, ISO date/time/datetime,
JSON и enum. Внешние ключи показываются как скалярные поля с подсказкой о целевой таблице,
если их не заменяет доступный редактор связи.
Если скрытое или read-only обязательное поле не имеет default, отключите создание
либо используйте адаптер, заполняющий поле через сервис приложения.
Несколько источников данных
admin.add_adapter(sqlalchemy_adapter, namespace="billing")
admin.add_adapter(tortoise_adapter, namespace="catalog")
# /admin/models/billing-orders/ и /admin/models/catalog-products/
При нескольких адаптерах передавайте adapter=adapter в register().
Конфликтующие имена требуют namespace или ModelOptions(slug=...).
Конкурирующее редактирование
Встроенные адаптеры атомарно отклоняют устаревшую форму: если запись или редактируемая
связь изменилась после открытия страницы, сохранение возвращает 409. Введённые
значения остаются в форме. Нажмите Перечитать запись, чтобы получить актуальную
версию перед новой правкой. Миграция БД и дополнительная колонка версии не нужны.
Ревизия действует в пределах настроенного времени сессии и привязана к пользователю,
таблице и полному первичному ключу. В ней находятся keyed fingerprints, а не значения полей.
Стандартный шаблон добавляет токен автоматически. При обновлении своего form.html
вставьте в форму рядом с CSRF-полем:
{% if revision_token %}
<input type="hidden" name="__revision" value="{{ revision_token }}">
{% endif %}
HTTP-клиент должен сначала получить форму GET-запросом и передать её __revision
при редактировании. Отсутствующая, неверная или истёкшая ревизия также возвращает 409.
После конфликта нельзя просто подставлять свежую ревизию без просмотра актуальной
записи. Защита относится к сохранению формы; удаление и свои действия сохраняют
прежние правила. Свои адаптеры подключают CheckedAdapter;
остальные используют собственную политику записи.
Настройка filter_fields и фильтрация по связанным записям описаны в разделе фильтров.
Контролы типов БД
Заголовок записи, секции формы и действия размещены в одном адаптивном контейнере
по центру. Короткие поля занимают две колонки, когда хватает места; многострочный
текст, JSON и редакторы связей — всю ширину. Подписи остаются над контролами,
а подсказки и ошибки проверки — рядом с соответствующим полем. Пустые поля чисел,
UUID, JSON и пароля показывают подсказки формата: они не задают значение и не заменяют
подпись. Значения только для чтения собраны в отдельной секции. Панель сохранения
и отмены остаётся доступной при прокрутке длинной формы. Одиночный и множественный выбор записей описан в
разделе связей; существующий календарь даты/времени — ниже.
| Тип БД/модели | Встроенный контрол |
|---|---|
| CHAR / VARCHAR, строки с ограничением | Однострочный input с обнаруженным maxlength |
| TEXT, Tortoise/Peewee TextField | Многострочная textarea на всю ширину |
| DATE | Календарь темы, локальное отображение, ISO; fallback нативного input |
| DATETIME / TIMESTAMP | Общий календарь со временем, необязательными долями секунды и UTC offset |
| TIME | Тот же компонент без календаря, с необязательными долями секунды и UTC offset |
| JSON / JSONB | Моноширинный редактор, подсветка, статус синтаксиса, форматирование |
| Integer / Decimal / float | Числовой input с целым или произвольным шагом |
| Boolean | Тогл с явным nullable-состоянием; fallback select |
| Enum | Select допустимых значений; для nullable есть пустой вариант |
| UUID | Текстовый input с серверной проверкой UUID |
| Binary / неизвестный | Read-only, кроме уже явно настроенных parser/template политик |
Тип TEXT определяется и SQL reflection, и ORM-адаптерами; ORM не обязательна.
Для собственного адаптера Field.multiline — необязательные метаданные с default False.
Логический kind остаётся string: поиск, фильтрация и разбор значений не меняются.
String() без длины остаётся однострочным; многострочность задаёт именно TEXT/TextField.
Существующий FieldOptions.template сохраняет приоритет.
Подсветка JSON работает локально через текстовые узлы, без внешнего редактора.
Форматировать JSON сохраняет запись чисел, повторяющиеся ключи и экранирование строк,
не преобразуя числа через JavaScript float. Статус синтаксиса не заменяет серверную
проверку типа/nullable. Подсветка отключается после 200 000 символов, форматирование —
после 40 уровней вложенности; обычная textarea продолжает работать.
DATE, DATETIME, TIME, nullable boolean, TEXT и JSON доступны для изменения в Schedules
браузерного демо. Обзор интерфейса показывает форму и редактор ячеек.
Формы, фильтры листинга, inline-редактирование и история используют один календарь
в теме админки. В формах и inline-редактировании значения доступны сразу: DATE —
одно поле даты, DATETIME — дата и время рядом, TIME — поле времени. Компактный
календарь не дублирует ввод даты и не требует отдельного «Применить»: выбор дня,
Сегодня или Сбросить меняет поле и закрывает календарь. Запись сохраняется
кнопкой Создать запись / Сохранить изменения всей формы. Escape и нажатие снаружи
закрывают просмотр календаря без изменения поля. Страница при открытии не прокручивается.
В фильтрах остаются Применить / Отмена для черновика выбора. Условие Между
открывает одну панель с двумя границами, включая время, если оно есть у поля.
Сбросить очищает обе границы. Период истории включает полные дни UTC.
Дни календаря поддерживают стрелки, Home/End и Page Up/Down; Alt+Down открывает
календарь поля в форме.
Контролы даты/времени не переводят значение в часовой пояс браузера. Z или явное
смещение ±HH:MM сохраняется отдельно; пустое смещение означает naive-значение.
Раздел Дополнительно в формах (Точность и часовой пояс в фильтрах)
раскрывает поля смещения и долей секунды (до шести цифр), чтобы ограничения нативного календаря не обрезали микросекунды.
Открытие формы не нормализует и не обрезает исходный timestamp. Необычные ISO-форматы,
смещения и ошибочно введённые значения остаются в текстовом input. Без JavaScript
дата/время остаются ISO-input, JSON — обычной textarea. Вид календаря зависит от браузера
и ОС. Серверный разбор, ограничения модели, права полей, CSRF и подписанные revision
по-прежнему обязательны. Встроенный скрипт разрешается существующим CSP-хешем страницы.
Готовый пример шаблона и parser есть в разделе контролов полей.
Группы таблиц
Задайте ModelOptions(group="Продажи"), чтобы сгруппировать таблицы в виде
Список таблиц на главной, глобальном поиске и панели выбора схемы БД. Для SQL reflection:
admin.configure("orders", options=ModelOptions(group="Продажи")). Таблицы без группы
попадают в Другие таблицы в каталоге главной и поиске. Права проверяются до
группировки: пустых и недоступных групп нет.
На главной выберите Список таблиц, чтобы открыть полный каталог с изначально
раскрытыми группами. Нажатие на заголовок или Enter/Пробел сворачивает группу. Метка
показывает число таблиц; ссылка содержит подпись и техническое имя, если оно отличается.
Браузер запоминает выбранный вид главной отдельно для пользователя и mount админки;
сворачивание групп в Списке таблиц действует в пределах текущего посещения.
Вид Карточки содержит закреплённые и недавно открытые таблицы, инструменты и свои
страницы. Найти таблицу (Ctrl/⌘ K) ищет по подписи, техническому имени и группе
на любой странице после входа.
Без JavaScript на главной доступен нативный раскрывающийся блок Все таблицы со
свёрнутыми группами: раскройте его и группу для перехода к таблице. Слева полоса значков
ведёт к инструментам админки. Оба вида главной и закрепление описаны
в обзоре интерфейса.
Булевы поля используют тоглы с управлением с клавиатуры. Для nullable есть отдельная
кнопка «Нет значения», сохраняющая различие false и SQL NULL. Read-only поля остаются
неизменяемыми; без JavaScript доступен исходный select.
Редактирование ячеек списка
Нажмите карандаш изменяемого столбца для редактирования без перехода к записи.
Нажатие на неинтерактивное значение или область строки открывает запись. Ссылки на
связанные записи, URL/копирование, раскрытие значений, карандаши и переключатели
выполняют своё действие. Тогл сохраняет изменение сразу.
ModelOptions(list_editable=None) (default) включает все подходящие столбцы;
list_editable=("name", "active") ограничивает набор; list_editable=() отключает режим.
Столбец должен отображаться и быть разрешён edit_fields и политиками поля. Скрытые,
read-only, generated и первичные ключи не редактируются. Внешние ключи и поля с собственными
шаблонами редактируются на странице записи. Переиспользуются встроенные контролы типов.
Сохранение явное; Отмена/Escape отбрасывает локальное изменение.
Endpoint требует права list/edit, проверяет ровно одно поле и CSRF, запускает хуки записи
и сохраняет атомарную защиту от устаревших изменений. Конфликт версии возвращает 409,
ошибка значения — 422 без очистки редактора. После конфликта обновите список. Собственному
адаптеру для защиты от конфликтов нужен CheckedAdapter, как и для полной формы.
Без JavaScript можно использовать полную страницу записи.
Булевы ячейки списка тоже отображаются тоглами. Изменяемые сохраняются сразу через
защищённый inline endpoint, read-only тоглы отключены. Для nullable есть кнопка ∅
и состояние «Нет значения». При ошибке/конфликте состояние не меняется, показывается
сообщение. Повторные нажатия блокируются на время сохранения.
Большие таблицы и время запросов
Для большой таблицы задайте ModelOptions(pagination="cursor"). Режим поддерживают
все встроенные адаптеры. Он читает одну дополнительную запись для навигации вперёд/назад
без точного COUNT(*) и растущего OFFSET. Нажмите заголовок скалярного столбца, включая
дату, для сортировки по возрастанию или убыванию без дополнительных настроек. При
одинаковых значениях полный первичный ключ, в том числе составной, задаёт порядок по
возрастанию. NULL идёт последним в обоих направлениях. Без выбора столбца порядок
задаётся первичным ключом. Для JSON, binary и неизвестных типов курсорная сортировка
недоступна. По умолчанию pagination="offset" сохраняет точное количество и номера страниц.
Курсоры подписаны, истекают через настроенное время сессии и привязаны к таблице,
пользователю, фильтрам, поиску, порядку и размеру страницы. Изменение поиска, фильтра или сортировки
начинает новый обход. Это не снимок БД: конкурентные вставки и удаления могут менять
страницы. Экспорт включает все подходящие записи в пределах своих лимитов независимо
от текущего курсора.
Пользовательский адаптер может реализовать WindowAdapter.list_window(model, query, after=..., before=...) из quite_admin.pagination. Результат — RecordWindow(records, has_next, has_previous, next_key, previous_key) с полными типизированными ключами.
Допускается одна граница и нулевой offset. Укажите необязательную возможность
supports_window_sorting = True, если реализация принимает query.order_by: границы
и проекции должны включать поле сортировки и все части PK. При равных значениях PK
сортируется по возрастанию, NULL идёт последним. У существующих пользовательских
адаптеров без этой возможности сохраняется сортировка по PK.
Метод не входит в обязательный Adapter API.
SQL reflection и SQLAlchemy/SQLModel также принимают query_timeout=2.0 в конструкторе
адаптера. Положительное число секунд ограничивает чтение в SQLite, PostgreSQL и MySQL;
None отключает лимит. SQLite использует progress handler на время операции, PostgreSQL —
локальный statement timeout, MySQL — временный лимит SELECT. Перед возвратом соединения
в пул настройки восстанавливаются либо соединение инвалидируется; ошибка возвращает
безопасный 504. Транзакции записи работают по-прежнему. Ожидание пула и рендеринг Python
в этот лимит не входят; при необходимости настройте их в приложении.
Общие лимиты чтения
Настройте общую очередь и таймаут операции непосредственно в админке с любым адаптером:
admin = Admin(
auth=auth_backend, secret_key=session_secret, adapters=[adapter],
query_timeout=3.0,
max_concurrent_reads=2,
)
Обе настройки по умолчанию равны None и сохраняют прежнее поведение. Положительный
таймаут включает ожидание слота, асинхронное получение соединения из пула и само чтение.
Превышение возвращает безопасный 504. Положительный целочисленный лимит параллелизма
общий для всех моделей/адаптеров этого Admin в одном процессе. Это не лимит между
воркерами: у каждого своя очередь. При общем пуле выбирайте лимит ниже его ёмкости,
оставляя соединения обычным запросам приложения.
Политика охватывает асинхронное обнаружение моделей, списки, курсорные страницы,
получение записи, чтение/подбор связей и экспорт, включая пакетные запросы. Она действует
только на чтения самой админки. Прямые вызовы адаптера, аутентификация, I/O сервисных
обработчиков и служебное хранилище сохраняют собственные политики. Записи и commit не
прерываются этим таймаутом. Сбой чтения истории после commit сохраняет успешный ответ
записи и выдаёт существующее безопасное предупреждение истории.
При отмене слот освобождается после очистки ресурсов адаптера. Синхронный поток сначала
заканчивает работу и возвращает соединение/слот, поэтому ответ 504 может прийти позже
таймаута. Для прерывания самого SQL также настройте query_timeout нативного
SQL/SQLAlchemy-адаптера, описанный выше. Асинхронные адаптеры, включая GINO, получают
отмену во время ожидания пула или выполнения запроса. Дополнительный класс-обёртка,
семафор и обработчик исключения таймаута не нужны.
Поля только для ввода в форме
Дополнительные поля формы не требуют фиктивных колонок в адаптере:
from quite_admin import Field, ModelOptions
admin.register(User, options=ModelOptions(
input_fields=(
Field("password1", kind="password", label="Пароль"),
Field("password2", kind="password", label="Повторите пароль"),
),
create_fields=("username", "password1", "password2"),
edit_fields=("username",),
))
Для полей ввода действуют преобразование типов, обязательность и списки разрешённых
полей создания/редактирования. Имена должны быть уникальными и не совпадать с полями
модели. Подписи, подсказки и контролы задаются через обычный fields={...}. Явное
объявление поля ввода разрешает ввод чувствительного имени без expose_sensitive_fields.
Схема адаптера не меняется. Поля ввода недоступны в списках, сортировке, поиске,
фильтрах, экспорте, названиях записей, диаграмме, отпечатках ревизии и деталях истории.
Они не заполняются из сохранённой записи и скрыты на страницах только для чтения.
Выбирайте формы через create_fields / edit_fields.
kind="password" создаёт доступный password-контрол с autocomplete="new-password".
Пароль не подставляется и не возвращается после ошибок валидации/конфликтов, в том числе
в контексте пользовательских шаблонов. Обязательный пароль проверяется на пустую строку
на сервере. Поля модели с явно заданным таким типом также исключаются из отображения
данных и экспорта. Password-контрол имеет приоритет перед пользовательским шаблоном поля.
Обработчик модели или сервисный адаптер должен обработать значения
ввода перед вызовом обычного адаптера:
проверить подтверждение, хешировать пароль и сохранить только реальные колонки модели.
Обычный адаптер не сохраняет поля ввода. См. сервисы приложения.