Миграция — SQL-файл, который изменяет структуру БД или её данные. Generate создаёт такие файлы, но не выполняет их в базе. Для применения используется мигратор: его запускают make migrate, make dev или контроллер развёртывания (deploy/control).
Примените изменения модели к dev-БД
- В UI генератора измените сущность, поля, связи или настройки, влияющие на схему.
- Откройте Generate, посмотрите изменения и выполните генерацию.
- Проверьте новую миграцию в
backend/migrationsперед применением к важным данным. - В корне dev-проекта выполните:
make up
make migrate
make dev тоже применяет ожидающие миграции перед запуском приложения. Повторный запуск не выполняет уже применённый SQL заново.
Какие файлы участвуют
| Файл или таблица | Назначение |
|---|---|
backend/migrations/manifest.json |
Общая последовательность system, generated и custom-миграций |
NNNNNN_system_*.sql |
Системная схема: например, пользователи и медиа |
NNNNNN_generated_*.sql |
Изменения, полученные из модели |
custom/NNNNNN_custom_*.sql |
Ваши SQL-изменения |
admingen_internal.schema_migrations |
Журнал выполненных шагов и контрольных сумм в PostgreSQL |
Номер определяет порядок выполнения. Каждый SQL-файл должен быть зарегистрирован в manifest.json: мигратор не применяет файл только потому, что тот лежит в папке.
Добавьте свою SQL-миграцию
После первого Generate из корня проекта:
make migration-new NAME=backfill_labels
Эквивалентная команда CLI:
admingen-cli migration create --project . --name backfill_labels
Команда создаст файл в backend/migrations/custom и зарегистрирует его под следующим свободным номером в manifest.json. БД на этом шаге не меняется. Откройте файл по пути из вывода команды, напишите SQL и сохраните в Git и SQL-файл, и изменённый manifest.
Пример заполнения нового nullable-поля у существующих записей:
UPDATE "articles"
SET "label" = "title"
WHERE "label" IS NULL;
Подставьте реальные таблицу и поля своего проекта. Затем примените миграцию через make migrate. При следующем Generate генератор продолжит общую последовательность и сохранит custom SQL.
Проверьте подключение и применённые миграции
Из корня dev-проекта:
make wait-db
make wait-db WAIT_DB_CMD='cd backend && APP_PROFILE=local go run ./cmd/migrate --verify'
Первая команда проверяет соединение через --check. Во второй вместо него указан --verify, чтобы проверить ещё и журнал миграций. Обе получают подключение из окружения проекта через Makefile и не изменяют БД.
--check проверяет только соединение с PostgreSQL. --verify проверяет, что ожидаемый журнал миграций полностью применён и согласован с файлами; SQL не исполняется. Успех — код завершения 0.
В работающем контейнере backend:
docker exec ИМЯ_КОНТЕЙНЕРА_BACKEND migrate --verify
Обязательные поля и старые записи
Добавление обычного поля с Required или включение Required у существующего поля не требует заранее заполнять все старые записи. Поддерживаемая миграция допускает отсутствие значений в прежних строках, а приложение продолжает читать их. В ответе API такое поле может быть null, хотя для создания и сохранения записей оно уже обязательно.
При сохранении старой записи действуют те же текущие требования, что и при создании новой. Если значений не хватает, заполните их в форме либо заранее своей SQL-миграцией, как в примере выше. Генератор не подставляет выдуманные значения ради прохождения миграции. Required — правило записи данных; оно не означает, что все исторические строки уже заполнены.
Это не снимает ограничения первичных и внешних ключей, уникальности и смены типов. Добавление новой Unique-колонки в уже созданную таблицу по-прежнему требует отдельного решения и обычной генерацией не поддерживается.
Миграции переводов
При включении Languages генератор создаёт рядом с основной таблицей таблицу <table>_translations. Существующие значения переводимых полей переносятся в основной язык. Запись сохраняет свой ID, а значения основного языка остаются и в исходных колонках.
Новые языки и Require all languages можно включить сразу, даже если записи уже есть. Сохраните настройки, выполните Generate, примените новую миграцию и пересоберите приложение. Отсутствующие старые переводы не блокируют миграцию. Их обязательность проверяется при последующем создании или сохранении записи. При смене основного языка недостающие переводы также могут остаться null.
Удаление языка из настроек не удаляет его сохранённые переводы. Rename поля, для которого уже сгенерировано хранение переводов, блокируется: нужен отдельный перенос данных. Подробный пример — Languages.
Новое поведение применяется к новым генерируемым миграциям. Уже выпущенные SQL-файлы автоматически не переписываются. Если старая миграция остановилась на проверке отсутствующих переводов, сначала разберите её состояние и историю во всех окружениях. Повторное Generate само по себе не пропустит этот блокирующий шаг.
В production
Для серверного обновления включите миграции в новый релиз и выполните deploy. Контроллер временно закроет доступ к приложению, создаст резервную копию, применит SQL из релиза и проверит готовность. Только после этого релиз станет текущим. Не применяйте к работающему серверу SQL из локальной папки отдельно от его релиза.
По умолчанию мигратору отводится 30 минут. Другое время можно задать через MIGRATION_TIMEOUT в формате длительности Go: например, 45m означает 45 минут. Перед увеличением лимита выясните, какой запрос задерживает миграцию.
Ошибки и исправления
| Ошибка | Что делать |
|---|---|
| PostgreSQL недоступен | Проверить make up, адрес, порт и параметры подключения |
| Checksum применённой миграции изменился | Вернуть исходный SQL из Git; исправление оформить новой миграцией |
| Журнал неполный или расходится с manifest | Сверить версию кода, SQL-файлы и источник дампа; не править журнал вручную |
| SQL новой миграции завершился ошибкой | Прочитать причину, исправить ещё не применённый SQL и повторить; предыдущие успешные шаги остаются применёнными |
SQL из одного файла и запись о его применении сохраняются одной транзакцией. После успешного применения хотя бы в одном окружении файл больше не редактируют. Иначе один номер миграции будет означать разный SQL на разных серверах. Исправление оформляйте новой миграцией.
Rollback образа не отменяет SQL и не восстанавливает удалённые данные. Для потенциально разрушительного изменения сначала сделайте backup и спланируйте совместимость старого и нового кода. Возврат данных выполняется через restore согласованной резервной точки.
Проверка ограничений внешних ключей
Если ограничение добавлено как NOT VALID, PostgreSQL уже проверяет новые изменения, но старые строки ещё нужно проверить отдельно. Посмотрите список и валидируйте выбранное ограничение из корня dev-проекта:
make constraints-list
make constraint-validate NAME=fk_users_role_id__roles_id
Имя возьмите из результата constraints-list, а не из примера. Для всех ожидающих ограничений:
make constraints-validate
Если проверка обнаружит строки, которые ссылаются на отсутствующие записи, исправьте эти данные и повторите проверку. Не удаляйте ограничение, чтобы скрыть ошибку. Команды выше берут dev-подключение из Makefile. На сервере проверку нужно проводить отдельно, заранее выбрав нужную версию кода и подключение к БД.