O‘chirish
Tasdiqlash sahifasi nima sodir bo‘lishini aniq hisoblaydi — kaskad tarzda o‘chiriladigan qatorlar, bog‘lanishi tozalangan holda saqlanadigan qatorlar va o‘chirishni butunlay bloklaydigan qatorlar.
Mavjud v0.1.0
Ko‘pchilik admin panellari «ishonchingiz komilmi?» deb so‘raydi-yu, oqibatini keyin bilib oladi. FastFort esa hech narsa yozilishidan oldin bog‘lanishlar grafigini aylanib chiqadi va har bir bog‘liq model bo‘yicha nima sodir bo‘lishini aniq bildiradi.
Uch xil natija
| Natija | Qachon | Sahifada nima yoziladi |
|---|---|---|
DELETE | ORM kaskadi yoki ON DELETE CASCADE | «bu ham o‘chiriladi» |
CLEAR | NULL qabul qiluvchi tashqi kalit yoki ON DELETE SET NULL | «bog‘lanishisiz saqlanadi» |
PROTECT | kaskadsiz NOT NULL tashqi kalit | o‘chirishni bloklaydi |
Har bir natija mapper va cheklovdan (constraint) o‘qib olinadi, taxmin qilinmaydi.
class Invoice(Base):
# Declared, so the plan reports DELETE.
lines: Mapped[list[InvoiceLine]] = relationship(
back_populates="invoice", cascade="all, delete-orphan"
)
class InvoiceLine(Base):
# NOT NULL with no database cascade → PROTECT.
invoice_id: Mapped[int] = mapped_column(sa.ForeignKey("invoices.id"))
class Zone(Base):
# Nullable with SET NULL → CLEAR.
hub_id: Mapped[int | None] = mapped_column(
sa.ForeignKey("hubs.id", ondelete="SET NULL")
)
U ORM nima qilishini bildiradi, sizning nimani nazarda tutganingizni emas
Aynan shu joyi muhim. cascade="all, delete-orphan"ni hech qachon e’lon
qilmagan model SQLAlchemy’ning standart xatti-harakatini oladi: bola
yozuvlar NULL qilinadi, o‘chirilmaydi.
FastFort buni ochiq-oydin ko‘rsatadi, ORM bajarmaydigan kaskadni bajaradi deb da’vo qilish o‘rniga. Agar tasdiqlash sahifasida «bog‘lanishisiz saqlanadi» deb yozilgan bo‘lsa-yu, siz kaskadni kutgan bo‘lsangiz — tasdiqlash to‘g‘ri, modelda esa e’lon yetishmayapti.
PROTECT bitta jumlada rad etadi
This customer cannot be deleted: 3 invoice lines would be left without an
invoice.
Yozish urinilgandan keyin paydo bo‘ladigan, ichida cheklov nomi bo‘lgan
IntegrityError emas.
Bola tomonidan aniqlanadi
Bog‘lanishlar reestr mapperlarini shu jadvalga yo‘naltirilgan «ko‘pdan-birga» munosabatlar bo‘yicha skanerlash orqali aniqlanadi — bola tomonidan, ota-modeldagi teskari havola orqali emas. Shu sababli teskari havolani hech qachon e’lon qilmagan modelning tashqi kaliti ham topiladi.
«Ko‘pdan-ko‘pga» munosabatlar ataylab chetlab o‘tiladi: mahsulotni
o‘chirish uning product_tags jadvalidagi qatorlarini olib tashlaydi,
teglarning o‘zini emas.
Kaskadlar kuzatiladi va halol hisoblanadi
Uch daraja chuqurlikkacha. Chuqurroq darajalarga quyi so‘rov (subquery) orqali yetib boriladi, yuqoridagi daraja tasodifan namuna sifatida olgan qatorlar orqali emas — aks holda ikkinchi son umumiy son sifatida chop etilgan qism bo‘lib qolardi.
Murakkab (kompozit) tashqi kalit rekursiyani to‘xtatadi.
Deleting 1 customer will affect:
Invoices 14 kept, without the link
Shipments 86 kept, without the link
Invoice lines 41 will be deleted too ← reached at depth 2, through
the invoice cascade
Namuna sifatida beshta qator nomlanadi. Hisoblash mingga yetganda to‘xtaydi
va millionlab qatorli jadval ustida COUNT(*) ishga tushirish o‘rniga
«1000+» sifatida ko‘rsatiladi.
Ikki marta tekshiriladi
Bir marta tasdiqlash sahifasida, va yozishdan darhol oldin yana bir marta — chunki tasdiqlash sahifasi bir soat davomida ochiq turgan bo‘lishi va shu vaqt ichida grafik o‘zgargan bo‘lishi mumkin. Xuddi shu ikkinchi tekshiruv ommaviy o‘chirish amalidan oldin ham ishga tushadi.
Qator ichidagi tugmalar
Har bir qatordagi o‘chirish tugmasi arzon, spesifikatsiya darajasidagi belgidan foydalanadi — biror bog‘lanish kaskad qiladimi yoki yo‘qmi — so‘rovsiz va hisoblovsiz. To‘liq reja faqat tasdiqlash sahifasi uchun hisoblanadi, chunki uni har bir qator uchun hisoblash jadvalning har bir qatori uchun bitta grafik aylanishini anglatardi.
Amal orqali o‘chirish
O‘rnatilgan delete amali xuddi shu rejani tanlangan har bir qator uchun
ishga tushiradi. Agar ulardan birortasi himoyalangan bo‘lsa, hech narsa
o‘chirilmaydi va sahifada qaysi biri ekani ko‘rsatiladi — ommaviy
o‘chirishning yarmi bajarilishi umuman bajarilmaganidan yomonroq, chunki
operator qaysi yarmi o‘chirilganini aniqlashga majbur bo‘ladi.
Yumshoq o‘chirish
Yuqoridagilarning barchasi deletening standart xatti-harakatini
tasvirlaydi: qator olib tashlanadi, va u bilan birga nima ketishini aytish
uchun graf aylanib chiqiladi. ModelAdmin.soft_delete_field buni torroq
narsaga almashtiradi — olib tashlash o‘rniga yozish, va aylanib chiqadigan
hech narsa yo‘q.
from fastfort import admin
class OrderAdmin(admin.ModelAdmin):
soft_delete_field = "deleted_at"
Loyihada allaqachon mavjud bo‘lgan nullable boolean yoki date/datetime
ustunni nomlang — va delete adapterning deletesini chaqirishni
to‘xtatadi: o‘rniga u belgini yozadi. Ustunning o‘zidan boshqa hech qanday
migratsiya, ikkinchi jadval yo‘q: savatga tashlangan qator hech qachon
doim turgan jadvalini tark etmaydi.
deletion_plan umuman chaqirilmaydi. Savatga tashlangan qator hech qayerga
ketmagan, shuning uchun unga ishora qiluvchi hech narsaga kaskad, tozalash
yoki bloklash kerak emas — ushbu sahifada yuqorida tasvirlangan har bir
natija endi bu quvur so‘ramaydigan savolga aylanadi. Tasdiqlash sahifasi
buni aks ettiradi: bog‘liq yozuvlar haqida ogohlantirish yo‘q, va “buni
qaytarib bo‘lmaydi” o‘rniga “uni Savatdan tiklash mumkin” deyiladi.
Ro‘yxat “Savat” ko‘rinishiga ega bo‘ladi (?trashed=1, va “Qo‘shish”
tugmasi yonida unga havola), u faqat belgilangan qatorlarni ko‘rsatadi va
O‘chirish o‘rniga Tiklashni taklif qiladi. Boolean belgisi
True/False qiymatlarini oladi; date yoki datetime belgisi esa savatga
tashlangan payt va Noneni oladi. Nullable faqat ikkinchi turdan talab
qilinadi, birinchisidan emas — chunki booleanning o‘zida ikkala holat
allaqachon mavjud.
Belgi ustunining o‘zi hech qachon yaratish yoki tahrirlash formasida ko‘rinmaydi. Bu o‘chirish quvurining o‘z ichki hisobidir, kimdir qo‘lda belgilashi kerak bo‘lgan qiymat emas.
Buni ko‘rish
Demo’da:
/admin/billing.invoice/1/delete DELETE — the lines cascade
/admin/billing.customer/1/delete CLEAR at two depths, and a DELETE at depth 2
/admin/logistics.hub/1/delete CLEAR — the zones are kept