Доступ к данным и совместимость

Границы запросов и идентификаторов

Встроенные адаптеры сверяют поля сортировки и поиска со скалярными метаданными
до построения запроса. Значения передаются параметрами драйвера; % и _ в поиске
означают обычные символы. В браузере сортировка доступна только по видимым полям.
Интерфейс не принимает произвольный SQL, выражения фильтрации или выражения колонок.

Query.validate(schema) требует неотрицательный offset, limit от 1 до 10 000,
булево направление сортировки и строку поиска до 4096 символов. Поиск допускает
только известные строковые поля. В UI ограничения строже: 100 записей и 500 символов.
Скалярные фильтры и фильтры связей применяются до подсчёта и пагинации.
Query.related требует явной поддержки адаптера и полных типизированных ключей цели;
обычный Query.validate(schema) отклоняет его без метаданных связанных схем.

Для get/update/delete нужен полный первичный ключ со всеми частями составного ключа.
Пустые ключи, NULL в ключе, операторы вида id__gt и неизвестные поля записи
отклоняются. Изменение первичного ключа не поддерживается. CRUD-таблица без PK вызывает
явную ошибку: библиотека не угадывает идентификатор строки.
Карточка промежуточной таблицы только на схеме может не иметь PK;
операции с записями у неё не появляются.

Скалярные значения

Формы сохраняют Decimal, UUID, enum, даты и время ISO, JSON и целые числа без
промежуточного преобразования в JavaScript Number. Контрактные тесты проверяют
знаковые 64-битные числа. Ограничения точности, диапазона и часового пояса задаёт БД.
SQLite с NUMERIC affinity может терять точность Decimal; для точных вычислений
используйте NUMERIC/DECIMAL в PostgreSQL или MySQL.

Пустой nullable-контрол преобразуется в None; строковое поле NOT NULL допускает
пустую строку. JSON поддерживает объекты, массивы и скаляры, если их допускает адаптер.
NaN и Infinity отклоняются. JSON null преобразуется в None, а ORM и колонка решают,
будет ли это SQL NULL или JSON null. Отдельного переключателя между ними в форме нет.
Reflection не восстанавливает Python-классы UUID и enum из обычного строкового столбца.

Транзакции и хуки

Каждая запись выполняется в собственной транзакции с rollback при любом исключении.
Поля и связи сохраняются вместе. Чтение закрывает connection/session без commit
транзакции записи. Engine и pool принадлежат приложению, сессии отдельных операций —
адаптеру. Не передавайте общую request-сессию вместо фабрики сессий.

SQL reflection выполняет defaults, constraints, триггеры и каскады БД, но не Python-хуки
ORM и не проверки приложения. Нативные адаптеры вызывают методы сохранения моделей;
обычные save-хуки и события ORM применяются. Явное удаление записи запускает каскады
ORM и БД. Отвязка связи не удаляет связанную запись; обнаруженная опасная отвязка
с delete-orphan блокируется. Для бизнес-правил и soft delete используйте сервисный адаптер.

Тесты проверяют rollback при ограничениях и ошибках хуков, перечитывание значений
после триггеров, каскады, составные PK/FK и продолжение работы после ошибки.

Встроенные формы используют подписанные ревизии и атомарную проверку
актуальности записи и состава редактируемых связей. Устаревшая запись откатывается
с 409; свои адаптеры подключают поддержку явно. История версий не хранится.

Проверки СУБД

ORM / интеграция Автоматические проверки Ограничения
SQL reflection Sync/async CRUD, фильтры/проекции, составные PK/FK, запись связей Физическая схема без Python-хуков; только чистые M:N junction-таблицы
SQLAlchemy 2.x Sync/async CRUD, общий контракт, ORM-хуки, связи Импортированные модели; dynamic/write-only/view-only связи только для чтения
SQLModel Отдельные sync/async-контракты настоящих моделей: CRUD, связи, фильтры, web-права, ревизии, схема БД, типы/defaults Адаптер SQLAlchemy; тесты с Pydantic 2; валидаторы приложения не становятся правилами записи админки автоматически
Peewee CRUD в worker, составные ключи, raw FK, M:N и 1:N/1:1 Нет async Peewee API; базой владеет приложение
Tortoise Async CRUD, raw FK, UUID, JSON, связи Инициализация в lifespan; нет контракта составного PK
GINO 1.0.x Родной async CRUD, общие контракты запросов и связей, составные PK/FK, типы PostgreSQL Только PostgreSQL/asyncpg; отдельное окружение SQLAlchemy 1.3; чистые таблицы M:N
Свои адаптеры Пример без ORM и структурные проверки протокола Хранилище, транзакции и optional-возможности проверяет автор приложения

Интеграционные наборы FastAPI и Litestar проверяют авторизацию, CRUD, связи,
ограничения полей и proxy prefixes. Проверяются закреплённое окружение и независимо
разрешаемые нижние/актуальные версии зависимостей.
Артефакты содержат точные версии; матрица не перебирает все возможные сочетания.

СУБД Адаптеры и режимы Подтверждение
SQLite SQL reflection sync/async, SQLAlchemy/SQLModel sync/async, Peewee, Tortoise Полный набор, CI CPython 3.11–3.15
PostgreSQL 17 Те же восемь режимов Локально проверены общие контракты CRUD, безопасности запросов, фильтров, связей, конкурентных изменений и схемы БД; отдельный CI job
MySQL 8.4 Те же восемь режимов Те же контракты проверены локально; отдельный CI job

Локальная проверка 2026-10-30:

Окружение Результат одного прогона
Точные нижние runtime-версии, CPython 3.11.18 и 3.15.2 828 passed, 2 skipped на каждой версии Python
Заново разрешённые актуальные runtime-версии, CPython 3.11.18 и 3.15.2 828 passed, 2 skipped на каждой версии Python
Актуальная Starlette 0.x, CPython 3.12.14 828 passed, 2 skipped
GINO minimum/latest, CPython 3.11.18 и 3.15.2, PostgreSQL 17 62 passed в каждом из четырёх прогонов
Закреплённые зависимости, PostgreSQL 17 / MySQL 8.4, CPython 3.12.14 534 passed на каждом сервере, включая 128 проверок SQLModel

Два пропуска полного набора — запускаемые отдельно тесты GINO и опциональная
проверка picologging; тесты SQLModel не пропускаются. Локальные JSON версий и JUnit XML
сохраняются в build/compatibility/; CI публикует отчёты в тех же форматах.
Команды воспроизведения приведены в совместимости зависимостей.

MySQL reflection не различает BOOLEAN и обычные колонки TINYINT(1).
Контрактная фикстура явно указывает Boolean через column_types;
обычные маленькие целые числа остаются числами.
Peewee 3.19 передаёт PyMySQL 1.2 устаревшие аргументы db / passwd. Тестовый подкласс
базы передаёт database / password, не подавляя предупреждений и не изменяя объекты
базы приложения. Запросы и транзакции по-прежнему выполняют Peewee и PyMySQL;
именно эта конфигурация подключения проверена серверной матрицей.

Матрица находится в .github/workflows/databases.yml и использует временные базы
сервисов. Для локальной проверки создайте одноразовую базу quite_admin_test:

uv sync --locked --group database-check
QUITE_ADMIN_TEST_DATABASE_URL='postgresql://quite_admin:local-test-password@127.0.0.1:55432/quite_admin_test' \
  uv run --group database-check pytest tests/test_adapters.py tests/test_adapter_safety.py tests/test_relations.py tests/test_sql_reflection.py tests/test_concurrency.py tests/test_filters.py tests/test_diagram.py tests/sqlmodel -q

Для MySQL нужен URL mysql://. Тесты создают и удаляют свои таблицы; запускайте их
последовательно. Fixture отклоняет нелокальные хосты и другие имена БД. Без переменной
окружения используются изолированные SQLite-файлы или память. Наличие workflow
не подтверждает успешный удалённый запуск. Проверка не охватывает каждую допустимую
версию драйвера, ORM и сервера.

GINO проверяется в отдельном окружении и CI-матрице, сохраняя существующие
проверки SQLAlchemy 2.x. Локальная проверка включает GINO 1.0.1, SQLAlchemy 1.3.24 и
asyncpg 0.32.0 с PostgreSQL 16, а также расширенные контракты конкурентных изменений,
фильтров и схемы БД на PostgreSQL 17. Отдельный CI job использует PostgreSQL 17;
его настройка сама по себе не подтверждает успешный удалённый запуск. Число тестов
меняется по мере развития контрактов; команды выше и workflow определяют состав набора.

Диапазоны целых чисел и отклонённые значения

Встроенные адаптеры передают диапазоны целых чисел через Field.minimum / maximum;
формы, фильтры, ключи и запись через адаптер проверяют их до обращения к БД. Сохраняется
объявленная разрядность SQL-типа, включая unsigned в MySQL, даже если SQLite допускает
более широкий диапазон. Некорректные значения формы или фильтра возвращают 422, сохраняя
ввод. Ожидаемые ошибки данных и переполнения скрывают подробности БД, а запись откатывается.
Поля пользовательских адаптеров не ограничены, пока адаптер не укажет границы. Точность
и остальные ограничения самой БД продолжают действовать.

Проекции на стороне БД

Все встроенные адаптеры применяют Query.columns в SELECT, включая чтение связей.
Без этого параметра возвращается полная запись. Списки и селекторы запрашивают только
поля для отображения и описания результатов, необходимые кандидаты подписи и ключи.
Экспорт читает выбранные поля, добавляя ключи внутри пакетных JOIN. Вызов
related_batch(..., columns=(...)) возвращает ровно выбранные поля цели; внутренние
ключи не добавляются в результат. Пользовательские адаптеры должны соблюдать проекции.