on_delete Django modellerinde ne yapar?


348

Django'ya oldukça aşinayım, ancak son zamanlarda on_delete=models.CASCADEmodellerle bir seçenek olduğunu fark ettim , aynı belgeleri aradım ama daha fazlasını bulamadım:

Django 1.9'da değiştirildi:

on_deleteartık ikinci konum bağımsız değişkeni olarak kullanılabilir (daha önce genellikle yalnızca bir anahtar kelime bağımsız değişkeni olarak geçilirdi). Bu, Django 2.0'da gerekli bir argüman olacaktır.

örnek bir kullanım örneği

from django.db import models

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

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

On_delete ne yapıyor? ( Sanırım model silinirse yapılacak işlemler )

Ne yapar models.CASCADE? ( belgelerdeki ipuçları )

Başka hangi seçenekler var ( tahminim doğruysa )?

Bunun belgeleri nerede bulunuyor?



1
Bu benzer sorunun metni aşağıda, bu cevapta listelenmiştir. "FYI, modellerde on_delete parametresi nasıl göründüğünden geriye dönük." Orijinal cevaplardan çok daha fazla ayrıntı sağlar.
HelenM

Yanıtlar:


785

Başvurulan nesne silindiğinde benimsenecek davranış budur. Django'ya özgü değildir, bu bir SQL standardıdır.

Bu tür bir olay meydana geldiğinde gerçekleştirilecek 6 olası işlem vardır:

  • CASCADE: Başvurulan nesne silindiğinde, kendisine başvuruda bulunan nesneleri de silin (Örneğin, bir blog gönderisini kaldırdığınızda, yorumları da silmek isteyebilirsiniz). SQL eşdeğeri: CASCADE.
  • PROTECT: Başvurulan nesnenin silinmesini yasakla. Silmek için manuel olarak referans veren tüm nesneleri silmeniz gerekir. SQL eşdeğeri: RESTRICT.
  • SET_NULL: Başvuruyu NULL olarak ayarlayın (alanın boş bırakılabilir olmasını gerektirir). Örneğin, bir Kullanıcıyı sildiğinizde, blog yayınlarında yayınladığı yorumları saklamak isteyebilirsiniz, ancak anonim (veya silinmiş) bir kullanıcı tarafından yayınlandığını söylemek isteyebilirsiniz. SQL eşdeğeri: SET NULL.
  • SET_DEFAULT: Varsayılan değeri ayarlayın. SQL eşdeğeri: SET DEFAULT.
  • SET(...): Belirli bir değer belirleyin. Bu, SQL standardının bir parçası değildir ve tamamen Django tarafından ele alınır.
  • DO_NOTHING: Muhtemelen çok kötü bir fikir, çünkü bu veritabanınızda bütünlük sorunları yaratacaktır (aslında var olmayan bir nesneye referans vererek). SQL eşdeğeri: NO ACTION.

Kaynak: Django belgeleri

Örneğin PostGreSQL belgelerine de bakınız .

Çoğu durumda, CASCADEbeklenen davranıştır, ancak her ForeignKey için, kendinize her zaman bu durumda beklenen davranışın ne olduğunu sormalısınız. PROTECTve SET_NULLgenellikle yararlıdır. CASCADENerede olmaması gerektiğini ayarlamak , tek bir kullanıcıyı silerek, tüm veritabanınızı basamaklı olarak silebilir.


Art arda gelen yönü netleştirmek için ek not

CASCADEEylemin yönünün birçok insan için net olmadığını fark etmek komik . Aslında, bundan haber komik sadeceCASCADE aksiyon açık değildir. Art arda sıralı davranışın kafa karıştırıcı olabileceğini anlıyorum, ancak bunun diğer eylemlerle aynı yönde olduğunu düşünmelisiniz . Dolayısıyla, CASCADEyönün sizin için net olmadığını düşünüyorsanız, aslında bu on_deletedavranış sizin için net değildir.

Veritabanınızda, bir yabancı anahtar temel olarak değeri yabancı nesnenin birincil anahtarı olan bir tamsayı alanı ile temsil edilir. Let Diyelim ki bir girdi var ki comment_A giriş için yabancı bir anahtar vardır, article_B . Comment_A girişini silerseniz , her şey yolundadır , article_B comment_A olmadan yaşardı ve silinirse rahatsız etmeyin. Ancak, eğer silmek article_B sonra, comment_A paniğe! Hiçbir zaman olmadan yaşamış article_B ve ihtiyaçları onu kendi niteliklerin 's parçası ( article=article_Bama ??? * article_B ** budur). Bu, bütünlük hatasınınon_delete nasıl çözüleceğini belirlemek için adımlarya söyleyerek:

  • "Hayır! Lütfen! Yapma! Sensiz yaşayamam!" ( PROTECTSQL dilinde söylenir )
  • "Tamam, ben senin değilsen, o zaman ben kimsem değilim" (ki SET_NULL)
  • "Güle güle dünya, madde_B olmadan yaşayamam" ve intihar ( CASCADEdavranış budur).
  • "Sorun değil, yedek sevgilim var, bundan sonra article_C'ye başvuracağım" ( SET_DEFAULThatta SET(...)).
  • "Gerçekle yüzleşemem, bana kalan tek şey olsa bile adını aramaya devam edeceğim!" ( DO_NOTHING)

Umarım basamaklı yönü daha net yapar. :)


19
Aptalca bir soru, ama çağlayan her zaman tek yönlü olmalı mı? Yani BlogPost silmek Comment için yabancı bir anahtar varsa BlogPostYorum silmek gerekir, ancak RDMS ne olursa olsun Yorum silmek BlogPost silmemelidir?
Anthony Manning-Franklin

20
@AnthonyManningFranklin Elbette. Silme sırasında yalnızca bir referans "bozulduğunda" tetiklenir. Bir yorumu sildiğinizde, referansı aynı anda sildiğinizde durum böyle değildir.
Antoine Pinsard

6
Soru aptalca değil; Bu açıklamaya da ihtiyacım var. Burada ilişkinin tek taraflı olduğunu varsayıyoruz, ilişkinin sahibi Comment, FK alanında masanın kim olduğunu , gerçek hayat modelinden bahsedersek BlogPost"sahibi" olduğunu varsayalım Comment. İyi.
WesternGun

3
Dikkat edilmesi gereken önemli bir nokta, Django'da bir on_delete ayarının veritabanının kendisinde bir ON DELETE deyimi oluşturmamasıdır. Belirtilen davranış (CASCADE gibi) yalnızca Django üzerinden yapılan silme işlemlerini etkiler, doğrudan veritabanında yapılan ham silme işlemlerini etkilemez.
JoeMjr2

2
Sondaki alıntılar doğrudan Roy Lichtenstein'ın çizgi roman panellerinden çekilmiş gibi görünüyor! Şaşırtıcı
hava saldırısı

42

on_deleteYöntem silmek modeli örneği bağlıdır modeli örnekleri ile ne Django anlatmak için kullanılır. (örneğin bir ForeignKeyilişki). on_delete=models.CASCADEYani sıra bağımlı modeller silmeye devam silme etkisi kaskad Django söyler.

İşte daha somut bir örnek. Bir var varsayalım Authorbir olan modelin ForeignKeybir de Bookmodeli. Şimdi, Authormodelin bir örneğini silerseniz , Django, Bookmodelin o örneğine bağlı olan model örnekleri ile ne yapacağını bilemez Author. on_deleteYöntem bu durumda ne yapacağını Django söyler. Ayar on_delete=models.CASCADE, Django'ya silme efektini arttırma talimatı verir, yani sildiğiniz Bookmodel örneğine bağlı olan tüm Authormodel örneklerini siler.

Not: on_deleteDjango 2.0'da gerekli bir argüman haline gelecektir. Eski sürümlerde varsayılan olarak kullanılır CASCADE.

İşte tüm resmi belgeler.


37

FYI, on_deletemodellerde parametre nasıl göründüğünden geriye doğru. on_deleteDjango'ya kaydınızda işaret ettiğiniz FK girişi silinirse ne yapacağını söylemek için bir modele Yabancı Anahtar (FK) koydunuz . Seçenekler bizim dükkan çoğu kullanmış PROTECT, CASCADEve SET_NULL. İşte anladığım temel kurallar:

  1. PROTECTFK'niz gerçekten değişmemesi gereken ve tablonuzun değişmesine kesinlikle neden olmaması gereken bir arama tablosuna işaret ederken kullanın . Herhangi biri bu arama tablosundaki bir girişi silmeye çalışırsa PROTECT, herhangi bir kayda bağlıysa silmelerini engeller. Ayrıca önler silmesini django senin bir görünüm tablo üzerinde bir girdi silindi sırf rekor. Bu son bölüm kritik. Birisi Cinsiyet tablomdaki "Kadın" cinsiyetini silecek olsaydı, CERTAINLY, bu benim cinsiyetim olan Kişi masamda bulunan tüm kişileri anında silmek istemezdim.
  2. CASCADEFK'niz bir "ebeveyn" kaydını işaret ettiğinde kullanın . Bir Kişi birçok PersonEthnicity girişleri olabilir Yani, eğer (o / o Amerikan Hint, Siyah ve Beyaz olabilir) ve Kişi yani edilir silinmiş, gerçekten ederim herhangi bir "çocuk" PersonEthnicity girişleri silinecek istiyorum. Kişi olmadan ilgisizdirler.
  3. Kullan SET_NULLne zaman do insanlar görünüm tablo üzerinde bir girişi silmek için izin istiyor, ancak yine de kaydını bulundurmak istiyoruz. Örneğin, eğer bir kişi bir liseye sahip olabilirse, ancak o lisenin arama masama gidip gitmemesi benim için hiç önemli değil on_delete=SET_NULL. Bu, Kişi kaydımı orada bırakacaktır; sadece Kişimdeki lise FK'sini boş olarak ayarlayacaktı. Açıkçası, null=Truebu FK'ye izin vermeniz gerekecek .

İşte her üç şeyi de yapan bir model örneği:

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'

Son bir nefis lokma olarak, eğer biliyor muydunuz yok belirtmek on_delete(veya hiç), varsayılan davranıştır CASCADE? Bu, birisi Cinsiyet tablonuzdaki bir cinsiyet girişini sildiyse, bu cinsiyete sahip Kişi kayıtlarının da silindiği anlamına gelir!

"Şüphe duyarsanız, hazırlayın" derdim on_delete=models.PROTECT. Ardından uygulamanızı test edin. Verilerinizi tehlikeye atmadan hangi FK'lerin diğer değerleri etiketlemesi gerektiğini hızlı bir şekilde anlayacaksınız.

Ayrıca, on_delete=CASCADEseçtiğiniz davranış buysa, taşıma işlemlerinizin hiçbirine gerçekten eklenmediğini belirtmek gerekir. Sanırım bu varsayılan on_delete=CASCADEolduğu için koymak hiçbir şey koymakla aynı şeydir.


12

Daha önce de belirtildiği gibi CASCADE, yabancı anahtarı olan kaydı siler ve silinmiş başka bir nesneye referans verir. Örneğin, bir emlak web siteniz varsa ve bir Şehri referans alan bir Mülkünüz varsa

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

ve şimdi Şehir veritabanından silindiğinde, ilişkili tüm Mülkler (örneğin o şehirde bulunan gayrimenkul) de veritabanından silinecektir

Şimdi SET_NULL veya SET_DEFAULT veya hatta DO_NOTHING gibi diğer seçeneklerin değerinden de bahsetmek istiyorum. Temel olarak, yönetim açısından, bu kayıtları "silmek" istersiniz. Ama onların yok olmalarını gerçekten istemiyorsun. Bir çok sebepten ötürü. Birisi yanlışlıkla veya denetim ve izleme için silmiş olabilir. Ve sade raporlama. Bu nedenle mülkü bir şehirden "ayırmanın" bir yolu olabilir. Yine, başvurunuzun nasıl yazıldığına bağlı olacaktır.

Örneğin, bazı uygulamalarda 0 veya 1 olan "silinmiş" bir alan vardır. Ve bunların tüm aramaları ve liste görünümleri vb. Raporlarda veya kullanıcının ön uçtan erişebileceği herhangi bir yerde görüntülenebilecek her şey, olan her şeyi hariç tutar deleted == 1. Ancak, silinen kayıtların listesini aşağı çekmek için özel bir rapor veya özel bir sorgu oluşturursanız ve daha sonra en son ne zaman değiştirildiğini (başka bir alan) ve kim tarafından (yani kim ve ne zaman sildiğini) görmek için .. yürütme açısından bu çok avantajlıdır.

Yanlışlıkla silme işlemlerini deleted = 0bu kayıtlar kadar basit bir şekilde geri alabileceğinizi unutmayın .

Demek istediğim, eğer bir işlevsellik varsa, bunun arkasında her zaman bir sebep vardır. Her zaman iyi bir sebep değil. Ama bir sebep. Ve genellikle de iyi bir tane.


3
CASCADE'in hangi yönde gerçekleştiğini açıkladığı için bu yardımcı oldu. SQL basamaklarını bilmiyorsanız kabul edilen cevap net değildir.
codescribblr

Teşekkür ederim, minnettarım!
George Mogilevsky

2
Bu cevabı vurguluyorum çünkü ilişki modelindeki yön hakkındaki şüpheme cevap veriyor
edepe

6

Sorunuzun cevabı: neden on_delete kullanıyoruz?

Bir ForeignKey tarafından başvurulan bir nesne silindiğinde, Django varsayılan olarak ON DELETE CASCADE SQL kısıtlamasının davranışını öykünür ve ayrıca ForeignKey içeren nesneyi siler. Bu davranış, on_delete bağımsız değişkeni belirtilerek geçersiz kılınabilir. Örneğin, boş bir ForeignKey'niz varsa ve başvurulan nesne silindiğinde bunun null olarak ayarlanmasını istiyorsanız:

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

On_delete için olası değerler django.db.models'de bulunur:

KASKAD: Kademeli siler; varsayılan.

PROTECT: django.db.IntegrityError alt sınıfı olan ProtectedError öğesini yükselterek başvurulan nesnenin silinmesini önleyin.

SET_NULL: ForeignKey değerini null olarak ayarlar ; bu yalnızca null değeri True olduğunda mümkündür.

SET_DEFAULT: ForeignKey'i varsayılan değerine ayarlayın; ForeignKey için bir varsayılan ayarlanmalıdır.


Ben sql ve django ile olgun değil gibi basit kelimeler benim için açıklığa kavuşturmak. Teşekkür ederim.
wm.p1us

3

Diyelim ki biri Kişi diğeri Şirketler adlı iki modeliniz var .

Tanım olarak, bir kişi birden fazla şirket oluşturabilir.

Bir şirketin sadece bir kişi olabileceği düşünüldüğünde, bir kişi silindiğinde o kişiyle ilişkili tüm şirketlerin de silinmesini isteriz.

Böylece, bir Kişi modeli oluşturarak başlıyoruz, bunun gibi

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

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

Sonra Şirketler modeli şöyle görünebilir

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)

on_delete=models.CASCADEModel firmalarda kullanımına dikkat edin . Yani, sahibi olan kişi (sınıf Personeli örneği) silindiğinde tüm şirketler silinir.


1

Mevcut bir çağlayana (yani bir şelaleye) bir FK eklemeyi düşünerek "CASCADE" işlevinin zihinsel modelinizi yeniden yönlendirin. Bu şelalenin kaynağı Birincil Anahtardır. Akışı siler.

Yani bir FK'nin on_delete değerini "CASCADE" olarak tanımlarsanız, bu FK'nin kaydını PK kaynaklı bir silme dizisine eklersiniz. FK'nin kaydı bu kademeye katılabilir veya katılmayabilir ("SET_NULL"). Aslında, FK içeren bir kayıt, silme akışını bile önleyebilir! "PROTECT" ile bir baraj inşa edin.


0

CASCADE kullanmak aslında Django'ya referans verilen kaydı silmesini söylemek anlamına gelir. Aşağıdaki anket uygulaması örneğinde: Bir 'Soru' silindiğinde, bu Sorunun sahip olduğu Seçenekler de silinecektir.

Örneğin Soru: Bizi nereden duydunuz? (Seçenekler: 1. Arkadaşlar 2. TV Reklamı 3. Arama Motoru 4. E-posta Tanıtımı)

Bu soruyu sildiğinizde, bu dört seçeneğin tümü de tablodan silinecektir. Hangi yönde aktığına dikkat edin. Soru Modelinde on_delete = models.CASCADE yazmanız gerekmez.

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.