Asosiy kontentga o‘tish

Maqolalar

Darslik · 2026-08-16 · ~5 daqiqa o‘qiladi · boshlang'ich

Alembic: ishlab turgan bazani buzmasdan o'zgartirish

#python #fastapi #database

Mundarija

Avvalgi qismda klinika API'sini qurdik va jadvallarni create_all bilan yaratdik. Oxirida bitta savol ochiq qolgan edi: Doctor modeliga yangi maydon qo'shsak nima bo'ladi? Sinab ko'ring — hech narsa bo'lmaydi. create_all faqat yo'q jadvalni yaratadi, borini o'zgartirmaydi. Lokalda bazani o'chirib qaytadan yaratish mumkin, lekin ichida haqiqiy yozuvlar turgan serverda bu yo'l yopiq.

Django'dan kelganlar uchun bu tanish muammo — u yerda makemigrations va migrate bor edi. FastAPI o'z ORM'iga ega bo'lmagani uchun bu ish alohida asbobga yuklangan: Alembic — SQLAlchemy oilasining rasmiy migratsiya vositasi, SQLModel bilan ham bemalol ishlaydi.

Sozlash: bir marta qilinadigan ish#

uv add alembic
uv run alembic init migrations

Ikkinchi buyruq loyihada yangi papka va fayl ochadi:

.
├── alembic.ini          ← asosiy sozlama
├── migrations/
│   ├── env.py           ← Alembic'ning "miyasi" — shu faylni sozlaymiz
│   └── versions/        ← migratsiya fayllari shu yerga tushadi
└── app/
    ├── models.py
    └── database.py

Ikki joyga tegamiz. Avval alembic.inida baza manzilini ko'rsatamiz:

# alembic.ini
sqlalchemy.url = sqlite:///clinic.db

Keyin env.pyga modellarimizni tanishtiramiz — Alembic "hozir baza qanday bo'lishi kerak"ligini bilishi uchun aynan shu kerak:

# migrations/env.py — mavjud faylda ikki joyni o'zgartiramiz

# 1) yuqoriga import qo'shamiz. Modellar import bo'lishi SHART —
#    aks holda Alembic ularni "ko'rmaydi"
from sqlmodel import SQLModel

from app import models  # noqa: F401

# 2) target_metadata = None qatorini almashtiramiz:
target_metadata = SQLModel.metadata

Bitta mayda, lekin tishlaydigan joy: migratsiya fayllari ichida SQLModel tiplari ishlatiladi, shuning uchun migrations/script.py.mako shabloniga bitta import qo'shib qo'ying:

# migrations/script.py.mako ichida, boshqa importlar yoniga:
import sqlmodel

Birinchi migratsiya#

Baza allaqachon create_all bilan yaratilgan bo'lsa ham, tarixni toza boshlaymiz — Alembic'ka "hozirgi holatni suratga ol" deymiz:

uv run alembic revision --autogenerate -m "boshlang'ich holat"

versions/ papkasida yangi fayl paydo bo'ladi. Uni ochib o'qing — ichida upgrade() (o'zgarishni qo'llash) va downgrade() (orqaga qaytarish) funksiyalari bor. Endi qo'llaymiz:

uv run alembic upgrade head

head — "eng oxirgi migratsiyagacha" degani. Alembic bazada alembic_version degan kichik jadval ochib, qaysi migratsiyada turganini o'sha yerda eslab qoladi.

Endi haqiqiy o'zgarish#

Modelga qabul narxini qo'shamiz:

# app/models.py
class Appointment(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    doctor_id: int = Field(foreign_key="doctor.id")
    patient_name: str
    starts_at: datetime
    price: int = Field(default=0)  # ← yangi maydon

Va ish tartibi har doim bir xil uch qadam:

# 1) farqni avtomatik aniqlash
uv run alembic revision --autogenerate -m "appointment'ga narx qo'shildi"

# 2) yaratilgan faylni O'QIB CHIQISH (pastdagi ogohlantirishga qarang)

# 3) qo'llash
uv run alembic upgrade head

Xato qildingizmi — bir qadam orqaga qaytish ham bor: alembic downgrade -1.

Autogenerate — yordamchi, hukmdor emas

--autogenerate modellar bilan bazani solishtirib farqni yozadi, lekin hamma narsani ko'ravermaydi: ustun nomi o'zgarganini "o'chir + yarat" deb tushunadi (ma'lumot yo'qoladi!), server_default'lar va ba'zi cheklovlarni o'tkazib yuboradi. Shuning uchun qoida qat'iy: yaratilgan migratsiya faylini har doim o'qib chiqing, ayniqsa ichida drop_column yoki drop_table ko'rsangiz bir to'xtang — bu odatda siz kutgan narsa emas.

SQLite bilan ishlayotganlarga bitta sozlama#

SQLite ALTER TABLEning ko'p turini bilmaydi — ustun o'chirish yoki o'zgartirishda Alembic xato beradi. Buning tayyor yechimi bor, env.pyda bitta parametr:

# migrations/env.py, context.configure(...) ichiga:
render_as_batch = True  # SQLite uchun: jadvalni qayta qurish orqali ALTER

Shu rejimda Alembic o'zgarishni "yangi jadval yasab, ma'lumotni ko'chirib, eskisini o'chirish" usulida bajaradi — SQLite'da boshqa iloj yo'q, PostgreSQL'ga o'tganingizda esa bu parametr shunchaki bekor ishlaydi.

Productionda qayerda turadi#

Migratsiya lokal o'yinchoq emas, deploy zanjirining doimiy qadami. CI/CD maqolasida ko'rgan deploy skriptida Django'ning migrate buyrug'i turgan edi — FastAPI loyihasida o'sha o'rinni shu buyruq egallaydi:

# scripts/deploy.sh ichida, xizmatni restart qilishdan OLDIN:
uv run alembic upgrade head

Tartib muhim: avval baza yangi holatga keladi, keyin yangi kod ishga tushadi.

Yakun#

Alembic bilan ishlash uch buyruqqa sig'adi: revision --autogenerate farqni yozadi, siz faylni o'qib tasdiqlaysiz, upgrade head qo'llaydi. Eng muhim odat — o'qish qadami: avtomatika yozgan migratsiya sizning niyatingizni emas, o'z tushunganini aks ettiradi, ikkalasining farqi esa ba'zan bir ustunlik ma'lumotga teng bo'ladi.

Shu bilan FastAPI juftligi yakunlandi. Keyingi o'quv maqolalari Django tomonga o'tadi: pytest-django bilan birinchi testni yozish — test nima uchun kerak degan savoldan ishlaydigan test faylgacha.