Связи
Связи редактируются в форме создания и на единой странице записи. Они сохраняются вместе
с полями кнопкой Создать запись или Сохранить изменения.
Для 1:1 и N:1 используется одиночный выбор, для 1:N и M:N — множественный.
Связанные записи должны уже существовать.
Значения сразу доступны для изменения при наличии прав. Пользователи с правом только на просмотр
видят ту же страницу с полями и связями только для чтения.
Поддержаны sync/async SQLAlchemy, sync/async SQL reflection, Peewee, Tortoise
и GINO 1.0.x / PostgreSQL.
ORM-адаптеры используют родные операции ORM, reflection — ограничения базы данных.
Обычные поля и все переданные связи сохраняются вместе либо полностью откатываются.
from quite_admin import ModelOptions
admin.register(Product, options=ModelOptions(
relations=("tags",),
readonly_relations=(),
record_label="name",
))
relations=None автоматически обнаруживает связи, relations=() скрывает редакторы связей
в формах создания и редактирования.
readonly_relations показывает выбранные записи без возможности изменения.
Укажите record_label у целевой модели, чтобы выбрать подпись в списке. По умолчанию
используются распространённые названия вроде name и title, затем первичные ключи.
relation_label исходного поля переопределяет подпись только для его выбора записей.
Без этой настройки редакторы связей используют обычную подпись целевой модели.
Исключённые поля никогда не используются как подписи.
Обе стороны должны быть зарегистрированы с одним адаптером. Для цели нужны права списка и просмотра.
Изменение обратного внешнего ключа дополнительно требует права редактирования цели
и доступности её FK-полей для записи. Скрытые/read-only поля источника и скрытые FK цели защищены.
Если связь отображается, обычный ввод её внешнего ключа заменяется выбором записи.
Отсутствующая в запросе связь сохраняется. Пустой выбор очищает связь, если FK допускает NULL.
Обязательный FK и delete-orphan нельзя отвязать через этот редактор: связанные записи не удаляются.
Для переноса обязательной дочерней записи выберите другого родителя в её собственной форме.
Неверные ключи, недоступные цели и нарушения ограничений отменяют всё сохранение.
Reflection обнаруживает простые M:N-таблицы с двумя FK и уникальным составным ключом.
Промежуточные таблицы с дополнительными данными требуют адаптера приложения.
Связи 1:1 определяются по первичному ключу, уникальному ограничению или полному уникальному
индексу. Частичный уникальный индекс не гарантирует общую кратность. Отражённые уникальные
ограничения MySQL представлены индексами и тоже учитываются.
SQLAlchemy использует объявленные relationship(); view-only и dynamic/write-only доступны только
для чтения. Peewee использует FK, backrefs и ManyToManyField, Tortoise — родные связи.
Собственные адаптеры могут реализовать экспортируемый RelationAdapter: relations(),
постраничный related() и атомарный save_relations(). Исходный скалярный протокол Adapter
не меняется. Метаданные представлены Relation и не зависят от шаблонов.
Локальное демо содержит Product–Tag (M:N), Customer–Order (1:N) и Customer–Profile (1:1).
Подписи внешних ключей
Внешний ключ на зарегистрированную таблицу становится ссылкой на страницу связанной
записи. По умолчанию показывается сохранённый ID. Задайте relation_label, чтобы
использовать видимое поле цели в ссылках списка. Если редактор связи включён,
та же настройка задаёт подпись выбранных записей и результатов поиска в формах
создания и редактирования:
admin.register(Link, options=ModelOptions(fields={
"user_id": FieldOptions(relation_label="username"),
"organization_id": FieldOptions(relation_label="name"),
}))
Импортируйте FieldOptions из quite_admin. Это работает с нативными адаптерами,
включая GINO, без formatter и собственного адаптера. Ссылки списка используют
эту подпись и при relations=(); эта настройка отключает редакторы связей в формах.
Для ссылки в списке нужны права списка и просмотра или изменения цели; для выбора
в форме — списка и просмотра. Скрытое или недоступное поле подписи заменяется ID
и не раскрывается ни в отображении, ни в поиске. Недоступная, отсутствующая или
незарегистрированная цель остаётся обычным значением в списке. NULL остаётся пустым.
Подписи списка загружаются одним ограниченным запросом адаптера на FK-столбец,
а не на каждую строку, с общими таймаутами чтения. Подписи не заменяют хранимые
значения: при сохранении, фильтрации, скалярном экспорте и в истории остаются исходные ID.
Поиск и выбор
Нажмите Выбрать запись для одиночной связи или Выбрать записи для множественной,
чтобы открыть поиск. Он использует видимые search_fields
целевой таблицы (по умолчанию — видимые строки). Если relation_label исходного
поля указывает на разрешённое строковое поле, поиск также использует его, даже
если оно не входит в search_fields цели или эта настройка пуста. Точный поиск
по ID работает независимо от search_fields; совпадения по тексту и ID
объединяются до пагинации. Результаты загружаются по 25 в стабильном
порядке PK; кнопки «Назад/Далее» доступны для всего набора кандидатов. Составной
ключ вводится полным JSON-объектом. Скрытый ключ не включает поиск по ID. Endpoint
возвращает ключи, разрешённые подписи, видимый ID и описания поисковых полей;
фильтры списков отображают дополнительный контекст. Выбранные записи
показаны отдельно от результатов поиска со счётчиком. Они сохраняются при поиске
и смене страницы; × убирает одну запись, а Очистить выбор — весь текущий выбор.
Для одиночной связи используются радиокнопки, для множественной — флажки; при
добавлении нескольких записей поиск остаётся открытым. Поддерживаются Tab, стрелки,
Space/Enter и Escape. Поиск не отправляет форму. Связи и поля сохраняются вместе
кнопкой «Сохранить изменения».
Компактная панель поиска ограничена по ширине и не растягивается на всю форму.
Она открывается поверх формы, не сдвигая её поля; если над селектором больше места,
раскрывается вверх. Нажатие на любую часть строки результата, включая текст,
выбирает одиночную запись или переключает её в множественном выборе.
Изменить выбор снова открывает панель для уже выбранной одиночной записи.
Нажатие снаружи, Escape или переход Tab за пределы компонента закрывают панель;
текущий выбор сохраняется.
В одной связи можно выбрать до 1000 записей. Больший существующий состав доступен
только для чтения, чтобы не потерять часть связей. Без JavaScript обычный селектор
содержит первые 25 кандидатов и текущий выбор; для поиска всех записей нужен JavaScript.
При сетевой ошибке выбор сохраняется и доступна кнопка «Повторить».
Endpoint вариантов при каждом запросе проверяет create/edit исходной таблицы,
list/view целевой, ограничения полей и право edit при изменении обратной связи.
Скрытые поля не участвуют в поиске. Встроенный скрипт загружается с того же origin
с SRI; CSP разрешает только его точный SHA-256 на формах с редактируемыми связями.
Inline-скрипты и произвольные скрипты с того же origin остаются запрещены. Свой
form.html может использовать стандартную разметку, picker_messages и
editor.endpoint; base.html автоматически подключает переданные scripts.
Фильтры списков позволяют находить связанные записи без изменения связи.
Схема базы данных показывает структуру связей и промежуточных
таблиц независимо от значений записей.