apa yang dilakukan on_delete pada model Django?

348

Saya cukup akrab dengan Django, tetapi baru-baru ini memperhatikan ada on_delete=models.CASCADEpilihan dengan model, saya telah mencari dokumentasi yang sama tetapi tidak dapat menemukan lebih dari:

Diubah dalam Django 1.9:

on_deletesekarang dapat digunakan sebagai argumen posisi kedua (sebelumnya biasanya hanya dilewatkan sebagai argumen kata kunci). Ini akan menjadi argumen yang diperlukan dalam Django 2.0.

contoh kasus penggunaan adalah

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'Manufacturer',
        on_delete=models.CASCADE,
    )
    # ...

class Manufacturer(models.Model):
    # ...
    pass

Apa yang dilakukan on_delete? ( Saya kira tindakan yang harus dilakukan jika model dihapus )

Apa yang models.CASCADEharus dilakukan ( ada petunjuk dalam dokumentasi )

Opsi apa lagi yang tersedia ( jika tebakan saya benar )?

Di mana dokumentasi untuk ini berada?

Semuanya
sumber
Ada juga jawaban untuk pertanyaan serupa di stackoverflow.com/questions/47914325/…
HelenM
1
Teks dari pertanyaan serupa ini sekarang tercantum, di bawah, pada jawaban ini. Itu dimulai "FYI, parameter on_delete dalam model mundur dari apa yang terdengar." Ini memberikan lebih banyak detail daripada jawaban asli.
HelenM

Jawaban:

779

Ini adalah perilaku untuk diadopsi ketika objek yang dirujuk dihapus. Ini tidak spesifik untuk Django, ini adalah standar SQL.

Ada 6 tindakan yang mungkin dilakukan ketika peristiwa tersebut terjadi:

  • CASCADE: Ketika objek yang direferensikan dihapus, juga menghapus objek yang memiliki referensi untuk itu (Ketika Anda menghapus posting blog misalnya, Anda mungkin ingin menghapus komentar juga). SQL setara: CASCADE.
  • PROTECT: Melarang penghapusan objek yang dirujuk. Untuk menghapusnya, Anda harus menghapus semua objek yang mereferensikannya secara manual. SQL setara: RESTRICT.
  • SET_NULL: Setel referensi ke NULL (mengharuskan bidang menjadi nullable). Misalnya, ketika Anda menghapus seorang Pengguna, Anda mungkin ingin menyimpan komentar yang dia posting di posting blog, tetapi mengatakan itu diposting oleh pengguna anonim (atau dihapus). SQL setara: SET NULL.
  • SET_DEFAULT: Tetapkan nilai default. SQL setara: SET DEFAULT.
  • SET(...): Tetapkan nilai yang diberikan. Yang ini bukan bagian dari standar SQL dan sepenuhnya ditangani oleh Django.
  • DO_NOTHING: Mungkin ide yang sangat buruk karena ini akan membuat masalah integritas di database Anda (merujuk objek yang sebenarnya tidak ada). SQL setara: NO ACTION.

Sumber: Dokumentasi Django

Lihat juga dokumentasi PostGreSQL misalnya.

Dalam kebanyakan kasus, CASCADEadalah perilaku yang diharapkan, tetapi untuk setiap ForeignKey, Anda harus selalu bertanya pada diri sendiri apa perilaku yang diharapkan dalam situasi ini. PROTECTdan SET_NULLseringkali bermanfaat. Pengaturan di CASCADEmana seharusnya tidak, berpotensi menghapus semua basis data Anda dalam kaskade, dengan hanya menghapus satu pengguna.


Catatan tambahan untuk memperjelas arah kaskade

Lucu melihat bahwa arah CASCADEtindakannya tidak jelas bagi banyak orang. Sebenarnya, itu lucu untuk melihat bahwa hanya dengan CASCADEtindakan tidak jelas. Saya mengerti perilaku kaskade mungkin membingungkan, namun Anda harus berpikir bahwa itu adalah arah yang sama dengan tindakan lainnya . Jadi, jika Anda merasa CASCADEarah itu tidak jelas bagi Anda, itu sebenarnya berarti bahwa on_deleteperilaku tidak jelas bagi Anda.

Dalam database Anda, kunci asing pada dasarnya diwakili oleh bidang bilangan bulat yang nilainya adalah kunci utama objek asing. Katakanlah Anda memiliki entri comment_A , yang memiliki kunci asing untuk entri article_B . Jika Anda menghapus entri comment_A , semuanya baik-baik saja, article_B digunakan untuk hidup tanpa comment_A dan jangan repot-repot jika itu dihapus. Namun, jika Anda menghapus article_B , maka comment_A panik! Itu tidak pernah hidup tanpa article_B dan membutuhkannya, itu adalah bagian dari atributnya ( article=article_B, tapi apa itu * article_B ** ???). Di sinilah on_deletelangkah-langkahnya, untuk menentukan cara mengatasi kesalahan integritas ini, baik dengan mengatakan:

  • "Tidak! Tolong! Jangan! Aku tidak bisa hidup tanpamu!" (yang dikatakan PROTECTdalam bahasa SQL)
  • "Baiklah, kalau aku bukan milikmu, maka aku bukan siapa-siapa" (yang dikatakan SET_NULL)
  • "Selamat tinggal dunia, saya tidak bisa hidup tanpa article_B" dan bunuh diri (ini adalah CASCADEperilaku).
  • "Tidak apa-apa, aku punya kekasih cadangan, aku akan mereferensikan article_C dari sekarang" ( SET_DEFAULT, atau bahkan SET(...)).
  • "Aku tidak bisa menghadapi kenyataan, aku akan terus memanggil namamu bahkan jika hanya itu yang tersisa untukku!" ( DO_NOTHING)

Saya harap ini membuat arah kaskade menjadi lebih jelas. :)

Antoine Pinsard
sumber
19
Sebuah pertanyaan konyol, tetapi kaskade harus selalu menjadi satu arah, bukan? Yaitu jika Comment memiliki kunci asing untuk BlogPostkemudian menghapus BlogPost harus menghapus Komentar, tetapi menghapus Komentar tidak boleh menghapus BlogPost, terlepas dari RDMS?
Anthony Manning-Franklin
20
@AnthonyManningFranklin Sure. On delete hanya dipicu ketika referensi "rusak". Yang tidak terjadi ketika Anda menghapus komentar, karena Anda menghapus referensi dalam waktu yang bersamaan.
Antoine Pinsard
6
Pertanyaannya bukan konyol; Saya perlu penjelasan itu juga. Jadi di sini kita menganggap relasinya uni-lateral, pemilik relasi adalah Comment, yang memiliki bidang FK di tabelnya, sementara BlogPost"memiliki" Commentjika kita berbicara tentang model kehidupan nyata. Baik.
WesternGun
3
Satu hal penting yang perlu diperhatikan adalah pengaturan on_delete di Django TIDAK membuat klausa ON DELETE dalam database itu sendiri. Perilaku yang ditentukan (seperti CASCADE) hanya akan memengaruhi penghapusan yang dilakukan melalui Django, dan bukan penghapusan mentah yang dilakukan secara langsung dalam database.
JoeMjr2
2
Penjelasan yang bagus. Mendapat dukungan dari saya!
Homunculus Reticulli
42

The on_deletemetode yang digunakan untuk memberitahu Django apa yang harus dilakukan dengan contoh model yang tergantung pada model contoh Anda hapus. (misalnya ForeignKeyhubungan). The on_delete=models.CASCADEmemberitahu Django untuk membuat efek penghapusan yaitu melanjutkan menghapus model dependen juga.

Inilah contoh yang lebih konkret. Asumsikan Anda memiliki Authormodel yang ada ForeignKeydalam Bookmodel. Sekarang, jika Anda menghapus instance Authormodel, Django tidak akan tahu apa yang harus dilakukan dengan instance Bookmodel yang bergantung pada instance Authormodel tersebut. The on_deleteMetode memberitahu Django apa yang harus dilakukan dalam kasus itu. Pengaturan on_delete=models.CASCADEakan menginstruksikan Django untuk membuat efek penghapusan yaitu menghapus semua Bookinstance model yang bergantung pada Authorinstance model yang Anda hapus.

Catatan: on_deleteakan menjadi argumen wajib dalam Django 2.0. Dalam versi yang lebih lama defaultnya adalah CASCADE.

Inilah seluruh dokumentasi resmi.

him229
sumber
37

FYI, on_deleteparameter dalam model mundur dari apa yang terdengar. Anda mengenakan on_deleteKunci Asing (FK) pada model untuk memberi tahu Django apa yang harus dilakukan jika entri FK yang Anda tunjuk pada catatan Anda dihapus. Pilihan toko kami telah menggunakan sebagian besar PROTECT, CASCADEdan SET_NULL. Berikut adalah aturan dasar yang saya temukan:

  1. Gunakan PROTECTsaat FK Anda menunjuk ke tabel pencarian yang benar-benar tidak boleh berubah dan yang pasti tidak menyebabkan meja Anda berubah. Jika ada yang mencoba menghapus entri pada tabel pencarian itu, PROTECTmencegah mereka menghapusnya jika dikaitkan dengan catatan apa pun. Ini juga mencegah Django dari menghapus catatan Anda hanya karena menghapus entri pada tabel pencarian. Bagian terakhir ini sangat penting. Jika seseorang menghapus jenis kelamin "Perempuan" dari tabel Gender saya, saya PASTI TIDAK ingin itu menghapus secara instan siapa saja dan semua orang yang saya miliki di tabel Person yang memiliki jenis kelamin itu.
  2. Gunakan CASCADEsaat FK Anda menunjuk ke catatan "orang tua". Jadi, jika Seseorang dapat memiliki banyak entri PersonEthnicity (dia bisa menjadi Indian Amerika, Hitam, dan Putih), dan Orang itu dihapus, saya benar - benar ingin entri PersonEthnicity "anak" dihapus. Mereka tidak relevan tanpa Pribadi.
  3. Gunakan SET_NULLketika Anda melakukan ingin orang diizinkan untuk menghapus entri pada tabel-up, tetapi Anda masih ingin mempertahankan catatan Anda. Sebagai contoh, jika seseorang dapat memiliki HighSchool, tetapi itu tidak masalah bagi saya jika SMA itu hilang di meja saya, saya akan mengatakan on_delete=SET_NULL. Ini akan meninggalkan catatan Orang saya di luar sana; itu hanya akan membuat FK sekolah menengah pada Orang saya menjadi nol. Jelas, Anda harus mengizinkan null=Truedi FK itu.

Berikut adalah contoh model yang melakukan ketiga hal:

class PurchPurchaseAccount(models.Model):
    id = models.AutoField(primary_key=True)
    purchase = models.ForeignKey(PurchPurchase, null=True, db_column='purchase', blank=True, on_delete=models.CASCADE) # If "parent" rec gone, delete "child" rec!!!
    paid_from_acct = models.ForeignKey(PurchPaidFromAcct, null=True, db_column='paid_from_acct', blank=True, on_delete=models.PROTECT) # Disallow lookup deletion & do not delete this rec.
    _updated = models.DateTimeField()
    _updatedby = models.ForeignKey(Person, null=True, db_column='_updatedby', blank=True, related_name='acctupdated_by', on_delete=models.SET_NULL) # Person records shouldn't be deleted, but if they are, preserve this PurchPurchaseAccount entry, and just set this person to null.

    def __unicode__(self):
        return str(self.paid_from_acct.display)
    class Meta:
        db_table = u'purch_purchase_account'

Sebagai berita gembira terakhir, tahukah Anda bahwa jika Anda tidak menentukan on_delete(atau tidak), perilaku defaultnya adalah CASCADE? Ini berarti bahwa jika seseorang menghapus entri gender pada tabel Gender Anda, setiap Orang yang mencatat gender tersebut juga akan dihapus!

Saya akan berkata, "Jika ragu, atur on_delete=models.PROTECT." Lalu, uji aplikasi Anda. Anda akan dengan cepat mengetahui FK mana yang harus diberi label nilai-nilai lain tanpa membahayakan data Anda.

Juga, perlu dicatat bahwa on_delete=CASCADEsebenarnya tidak ditambahkan ke migrasi Anda, jika itu adalah perilaku yang Anda pilih. Saya kira ini karena itu adalah default, jadi menempatkan on_delete=CASCADEadalah hal yang sama dengan tidak meletakkan apa pun.

Helen
sumber
12

Seperti disebutkan sebelumnya, CASCADE akan menghapus catatan yang memiliki kunci asing dan referensi objek lain yang telah dihapus. Jadi misalnya jika Anda memiliki situs web real estat dan memiliki Properti yang mereferensikan Kota

class City(models.Model):
    # define model fields for a city

class Property(models.Model):
    city = models.ForeignKey(City, on_delete = models.CASCADE)
    # define model fields for a property

dan sekarang ketika Kota dihapus dari basis data, semua Properti terkait (mis. real estat yang terletak di kota itu) juga akan dihapus dari basis data

Sekarang saya juga ingin menyebutkan nilai dari opsi lain, seperti SET_NULL atau SET_DEFAULT atau bahkan DO_NOTHING. Pada dasarnya, dari perspektif administrasi, Anda ingin "menghapus" catatan-catatan itu. Tetapi Anda tidak benar-benar ingin mereka menghilang. Untuk banyak alasan. Seseorang mungkin telah menghapusnya secara tidak sengaja, atau untuk audit dan pemantauan. Dan pelaporan biasa. Jadi itu bisa menjadi cara untuk "memutuskan" properti dari Kota. Sekali lagi, itu akan tergantung pada bagaimana aplikasi Anda ditulis.

Misalnya, beberapa aplikasi memiliki bidang "dihapus" yaitu 0 atau 1. Dan semua pencarian dan tampilan daftar dll, apa pun yang dapat muncul dalam laporan atau di mana pun pengguna dapat mengaksesnya dari ujung depan, mengecualikan apa pun yang ada deleted == 1. Namun, jika Anda membuat laporan khusus atau permintaan khusus untuk menarik daftar catatan yang dihapus dan bahkan lebih untuk melihat kapan terakhir diubah (bidang lain) dan oleh siapa (yaitu siapa yang menghapusnya dan kapan) .. itu sangat menguntungkan dari sudut pandang eksekutif.

Dan jangan lupa bahwa Anda dapat mengembalikan penghapusan tidak disengaja sesederhana deleted = 0untuk catatan-catatan itu.

Maksud saya adalah, jika ada fungsi, selalu ada alasan di baliknya. Tidak selalu alasan yang bagus. Tapi alasan. Dan seringkali yang bagus juga.

George Mogilevsky
sumber
3
Ini sangat membantu karena memperjelas ke arah mana CASCADE terjadi. Jawaban yang diterima tidak jelas jika Anda tidak terbiasa dengan kaskade SQL.
codescribblr
Terima kasih :) sangat dihargai!
George Mogilevsky
2
Saya memilih jawaban ini karena menjawab keraguan saya tentang arah dalam model hubungan
edepe
6

Inilah jawaban untuk pertanyaan Anda yang mengatakan: mengapa kami menggunakan on_delete?

Ketika suatu objek yang dirujuk oleh ForeignKey dihapus, Django secara default mengemulasi perilaku kendala SQL ON DELETE CASCADE dan juga menghapus objek yang berisi ForeignKey. Perilaku ini dapat diganti dengan menetapkan argumen on_delete. Misalnya, jika Anda memiliki ForeignKey yang dapat dibatalkan dan Anda ingin agar null ditetapkan ketika objek yang dirujuk dihapus:

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)

Nilai yang mungkin untuk on_delete ditemukan di django.db.models:

CASCADE: Cascade delete; default.

PROTECT: Mencegah penghapusan objek yang direferensikan dengan menaikkan ProtectedError, subkelas django.db.IntegrityError.

SET_NULL: Tetapkan ForeignKey null; ini hanya mungkin jika null Benar.

SET_DEFAULT: Tetapkan ForeignKey ke nilai defaultnya; default untuk ForeignKey harus ditetapkan.

Sonia Rani
sumber
Kata-kata sederhana membuatnya jelas bagi saya karena saya tidak dewasa dengan sql dan Django juga. Terima kasih.
wm.p1us
2

Katakanlah Anda memiliki dua model, satu bernama Person dan satu lagi bernama Perusahaan .

Menurut definisi, satu orang dapat membuat lebih dari satu perusahaan.

Mengingat sebuah perusahaan dapat memiliki satu dan hanya satu orang, kami ingin ketika seseorang dihapus maka semua perusahaan yang terkait dengan orang itu juga akan dihapus.

Jadi, kita mulai dengan membuat model Person, seperti ini

class Person(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.id+self.name

Kemudian, model Perusahaan dapat terlihat seperti ini

class Companies(models.Model):
    title = models.CharField(max_length=20)
    description=models.CharField(max_length=10)
    person= models.ForeignKey(Person,related_name='persons',on_delete=models.CASCADE)

Perhatikan penggunaan on_delete=models.CASCADEdalam Perusahaan model. Yaitu untuk menghapus semua perusahaan ketika orang yang memilikinya (instance dari class class) dihapus.

Tiago Martins Peres 李大仁
sumber
1

Ubah orientasi model mental Anda dari fungsionalitas "CASCADE" dengan memikirkan untuk menambahkan FK ke kaskade yang sudah ada (yaitu air terjun). Sumber air terjun ini adalah Kunci Utama. Menghapus mengalir ke bawah.

Jadi, jika Anda mendefinisikan on_delete FK sebagai "CASCADE," Anda menambahkan catatan FK ini ke kaskade penghapusan yang berasal dari PK. Catatan FK dapat berpartisipasi dalam kaskade ini atau tidak ("SET_NULL"). Bahkan, catatan dengan FK bahkan dapat mencegah aliran penghapusan! Bangun bendungan dengan "PROTECT."

menyenangkan
sumber
0

Menggunakan CASCADE berarti memberi tahu Django untuk menghapus catatan yang dirujuk. Dalam contoh aplikasi jajak pendapat di bawah ini: Ketika 'Pertanyaan' dihapus, itu juga akan menghapus Pilihan yang dimiliki Pertanyaan ini.

mis. Pertanyaan: Bagaimana Anda mendengar tentang kami? (Pilihan: 1. Teman 2. Iklan TV 3. Mesin Pencari 4. Promosi Email)

Saat Anda menghapus pertanyaan ini, itu juga akan menghapus keempat pilihan ini dari tabel. Perhatikan arah mana yang mengalir. Anda tidak harus memakai on_delete = models.CASCADE di Model Pertanyaan, masukkan ke dalam Pilihan.

from django.db import models



class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.dateTimeField('date_published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_legth=200)
    votes = models.IntegerField(default=0)
Kunal
sumber