Доступ к данным и совместимость
Границы запросов и идентификаторов
Встроенные адаптеры сверяют поля сортировки и поиска со скалярными метаданными
до построения запроса. Значения передаются параметрами драйвера; % и _ в поиске
означают обычные символы. В браузере сортировка доступна только по видимым полям.
Интерфейс не принимает произвольный 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=(...)) возвращает ровно выбранные поля цели; внутренние
ключи не добавляются в результат. Пользовательские адаптеры должны соблюдать проекции.