Контролы полей и собственные виджеты

Контрол выбирается по метаданным адаптера: строки с ограничением используют input,
TEXT — textarea, даты и время — общий календарь, JSON — локальный редактор,
boolean — переключатель с учётом NULL, enum — select. Полная
таблица контролов описывает точность и работу без JavaScript.

Встроенное отображение значений

Выберите отображение без изменения типа в БД и собственного шаблона:

admin.configure("links", options=ModelOptions(fields={
    "long_url": FieldOptions(display="url"),
    "hash": FieldOptions(url_template="https://short.example/{value}"),
    "metadata": FieldOptions(display="json"),
    "description": FieldOptions(display="multiline"),
}))
Настройка Поведение
display=None или "text" Обычное отображение; по умолчанию
display="url" Открытие абсолютного HTTP(S) URL и копирование значения
url_template="https://short.example/{value}" Сборка URL для отображения; включает display="url"
display="json" JSON с отступами; некорректный текст остаётся экранированным текстом
display="multiline" Сохранение переводов строк и раскрытие длинных значений в списке

Контролы работают в списках и read-only полях карточки. JSON и длинный текст
раскрываются нативным details; карточка показывает полный текст. У изменяемого URL
также есть предпросмотр со ссылкой и копированием рядом с обычным input. Поля с явно
заданным расширенным отображением редактируются в карточке; остальные колонки
сохраняют inline-редактирование. Если сервис изменил такое поле при inline-правке
другой колонки, его отображение также обновляется.

url_template принимает только {value} в абсолютном HTTP(S) URL. Отформатированное
скалярное значение кодируется как один компонент URL: abc/123 превращается в
abc%2F123. Настройка влияет только на отображение: форма, запись, фильтры, сортировка,
экспорт и история сохраняют исходное значение. Существующий formatter продолжает
работать на прежних поверхностях; display="url" может использовать его результат.
Другие поля записи и Python-выражения в URL-шаблонах не вычисляются. Вместе с
url_template оставьте display незаданным или укажите "url"; другие сочетания
считаются ошибкой конфигурации.

Ссылки допускают только абсолютные HTTP(S) адреса, открываются с noopener noreferrer
и экранируются. Относительные и небезопасные адреса показываются текстом. JSON и текст
не интерпретируются как HTML. Копирование использует clipboard API браузера; если
доступ закрыт, значение выделяется для ручного копирования. Ссылки и раскрытие работают
без JavaScript; кнопки копирования появляются после загрузки встроенного скрипта.
Значения не сохраняются в browser storage и не отправляются внешним сервисам.
Правила видимости, паролей и read-only сохраняются. Скрипт соблюдает обычный CSP/SRI
и не требует регистрации assets в приложении.

Выбор способа настройки

Задача Настройка
Изменить подпись, подсказку или read-only FieldOptions(label=..., help_text=..., read_only=True)
Разобрать или отформатировать прикладное значение FieldOptions(parser=..., formatter=...)
Отрисовать свой input FieldOptions(template="counter.html") и Admin(templates_dir="templates")
Запросить пароль или значение, отсутствующее в модели ModelOptions(input_fields=(Field(...),)) и handler записи
Проверить форму через Pydantic 2 create_schema / update_schema и extra pydantic
Изменить компоновку и цвета Theme, CSS с того же origin и шаблоны

Реестр именованных виджетов и автоматическая регистрация своего JavaScript отсутствуют.
Используйте описанные расширения; пользовательские шаблоны соблюдают существующий CSP.

Отрисовка и разбор своего input

Для целого числа в виде #7 настройте поле таблицы:

from quite_admin import FieldOptions, ModelOptions

admin.configure("products", options=ModelOptions(fields={
    "stock": FieldOptions(
        label="Stock", help_text="Use # followed by a whole number",
        parser=lambda raw: int(raw.removeprefix("#")),
        formatter=lambda value: "#" + str(value),
        template="counter.html",
    ),
}))

Создайте templates/counter.html в настроенном templates_dir:

{% set described_by %}{% if help and help.help_text %}help-{{ field.name }} {% endif %}{% if errors.get(field.name) %}error-{{ field.name }}{% endif %}{% endset %}
<input id="field-{{ field.name }}" name="{{ field.name }}" type="text"
       value="{{ values.get(field.name, '') }}"
       aria-invalid="{{ 'true' if errors.get(field.name) else 'false' }}"
       {% if described_by|trim %}aria-describedby="{{ described_by|trim }}"{% endif %}>

Внешняя форма добавляет подпись, подсказку, ошибку, CSRF и ревизию. Include получает
field, help, values, errors, editable и общий контекст шаблона. Сохраняйте
autoescape, отправляйте одно каноническое значение под field.name и поддерживайте
управление с клавиатуры. Parser возвращает нативное значение или выбрасывает
ValueError: сообщение становится ошибкой поля. Formatter возвращает экранируемый текст.

Свой шаблон используется только для изменяемых полей формы. Такие поля редактируются
на полной странице записи, без inline-правок. Шаблон и parser не предоставляют прав
на скрытые/read-only поля и не отменяют ограничения адаптера.

Пароли и поля только для форм

Объявите Field("password", kind="password") в input_fields и handler, который проверяет
и хеширует пароль, сохраняя реальные колонки модели. Контрол пароля остаётся пустым
после ошибок и имеет приоритет над своим шаблоном. Поля только для форм исключены
из списков, поиска, экспорта, ревизий и деталей истории.
Полный контракт описан в разделе полей только для форм.

Схема Pydantic проверяет форму после прав, CSRF и скалярного разбора, до hooks и записи.
Она не создаёт дополнительные контролы. Для частичных/inline-изменений используйте
необязательные поля update-схемы; см. сервисы и схемы форм.