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

NatijaQachonSahifada nima yoziladi
DELETEORM kaskadi yoki ON DELETE CASCADE«bu ham o‘chiriladi»
CLEARNULL qabul qiluvchi tashqi kalit yoki ON DELETE SET NULL«bog‘lanishisiz saqlanadi»
PROTECTkaskadsiz NOT NULL tashqi kalito‘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