Перейти к содержимому
Alembic + SQLAlchemy + PostgreSQL (Docker) | Мини‑курс [3]

Alembic + SQLAlchemy + PostgreSQL (Docker) | Мини‑курс [3]

0. Исходная точка (важно)

Перед этим уроком у вас уже должна быть применена первая миграция (таблица users создана):

alembic current
alembic history
Ожидаемо: alembic current показывает ревизию первой миграции (или уже head, если вы ранее делали урок 3). В БД есть таблицы users и alembic_version.

1. Создаем вторую миграцию

Для примера мы меняем схему: добавляем колонку full_name в users. Сначала меняем модель, затем создаем миграцию через --autogenerate.

1.1. Меняем модель

Откройте app/models.py и добавьте поле:

from sqlalchemy import String
from sqlalchemy.orm import Mapped, mapped_column

from app.db import Base


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)

    # Новая колонка (для учебного примера допускаем NULL)
    full_name: Mapped[str | None] = mapped_column(String(200), nullable=True)

Почему nullable=True: таблица уже существует и может содержать строки. Новая колонка без значения по умолчанию и с nullable=False часто приводит к ошибке (БД потребует заполнить значение для существующих строк).

1.2. Генерируем файл второй миграции

alembic revision --autogenerate -m "add full_name to users"

После команды в папке alembic/versions/ появится новый файл, например:

alembic/versions/2b1c3d4e5f6a_add_full_name_to_users.py

1.3. Проверяем содержимое миграции

Внутри должно быть что-то похожее:

from alembic import op
import sqlalchemy as sa

def upgrade() -> None:
    op.add_column("users", sa.Column("full_name", sa.String(length=200), nullable=True))

def downgrade() -> None:
    op.drop_column("users", "full_name")
Что важно: у второй миграции down_revision будет указывать на первую миграцию. Это “цепочка” миграций.

2. Применяем вторую миграцию

Применить все миграции до последней (включая вторую):

alembic upgrade head

2.1. Проверяем, что колонка появилась

docker exec -it alembic_course_db psql -U app_user -d app_db
\d users
\q
Ожидаемо: в users есть колонка full_name.

2.2. Проверяем, что текущая версия — head

alembic current

3. Откат второй миграции

Здесь два способа: откат на один шаг -1 или откат до конкретной ревизии. Для второй миграции чаще всего используют -1.

3.1. Откат на один шаг назад

alembic downgrade -1

Что произойдет: Alembic возьмет последнюю примененную миграцию (вторую) и выполнит ее downgrade(), то есть удалит колонку full_name.

3.2. Проверяем, что откат сработал

docker exec -it alembic_course_db psql -U app_user -d app_db
\d users
\q
Ожидаемо: колонки full_name снова нет. alembic current показывает ревизию первой миграции.
alembic current

3.3. Откат до конкретной ревизии (альтернатива)

Посмотреть историю:

alembic history

Откатиться до ID первой миграции (пример):

alembic downgrade 7f3b9b1e0f2a

ID у вас будет другим — используйте свой из alembic history.

4. Повторно применяем вторую миграцию

Вернуться вперед снова:

alembic upgrade head

Проверить:

docker exec -it alembic_course_db psql -U app_user -d app_db
\d users
\q
Ожидаемо: колонка full_name снова на месте.

5. Возвращение к “нулевому состоянию”

В терминах Alembic “нулевое состояние” — это base, то есть состояние до первой миграции. Это не “очистка данных”, а именно откат схемы по миграциям.

5.1. Откатить всё до base

alembic downgrade base

5.2. Что в итоге будет в БД

  • таблица users будет удалена (потому что первая миграция при откате делает op.drop_table("users"))
  • таблица alembic_version может остаться, но будет указывать на отсутствие версии (поведение зависит от версии/настроек; обычно запись версии очищается)

5.3. Проверка

docker exec -it alembic_course_db psql -U app_user -d app_db
\dt
\q
Осторожно: downgrade base удаляет таблицы, созданные миграциями. Для учебной БД это нормально. Для продакшена — делать только при полной уверенности и наличии бэкапа.

5.4. Вернуться из “нуля” обратно к актуальному состоянию

alembic upgrade head
Ожидаемо: снова созданы users и добавлена колонка full_name (если обе миграции существуют).

6. “Удаление второй миграции”: что это значит на практике

Сценарий A: миграция еще НЕ применялась нигде (безопаснее)

  1. Удалите файл второй миграции из alembic/versions/.
  2. Убедитесь, что БД не ушла вперед: alembic current должен показывать первую миграцию.
  3. Если вы уже применили вторую миграцию локально — сначала откатите:
    alembic downgrade -1
  4. После этого можно удалить файл миграции и создать новый корректный.

Сценарий B: миграция уже применена в общей среде/у коллег (опасно)

Если миграция уже “уехала” в общий репозиторий и была применена на другой БД, то удалять файл миграции нельзя, потому что история схемы станет “дырявой”. Правильный путь — создать новую миграцию, которая исправит/отменит изменения.

Правило команды: не переписывайте историю миграций, если она уже используется другими базами. Вместо этого — добавляйте следующую миграцию.

7. Команды для контроля: где мы в миграциях

КомандаЗачем
alembic history Посмотреть всю цепочку миграций (ID и сообщения).
alembic current Узнать, какая миграция применена сейчас.
alembic upgrade head Дойти до последней миграции.
alembic downgrade -1 Откатить одну миграцию назад (для “второй миграции” — самый типичный шаг).
alembic downgrade base Откатить всё в “ноль” (до первой миграции).

Ссылки по теме

Конспект:
Понедельник, 05 января 2026
Alembic + SQLAlchemy + PostgreSQL (Docker) | Мини‑курс [3]