co robi on_delete w modelach Django?

348

Znam Django, ale ostatnio zauważyłem, że istnieje on_delete=models.CASCADEopcja dla modeli, szukałem dokumentacji dla tego samego, ale nie mogłem znaleźć nic więcej niż:

Zmieniono w Django 1.9:

on_deletemoże być teraz używany jako drugi argument pozycyjny (wcześniej był zwykle przekazywany tylko jako argument słowa kluczowego). Będzie to wymagany argument w Django 2.0.

przykładowy przypadek użycia to

from django.db import models

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

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

Co robi on_delete? ( Myślę, że działania, które należy wykonać, jeśli model zostanie usunięty )

Co ma models.CASCADEzrobić? ( wszelkie wskazówki w dokumentacji )

Jakie inne opcje są dostępne ( jeśli moje przypuszczenie jest prawidłowe )?

Gdzie znajduje się dokumentacja do tego?

Wszystko to
źródło
Odpowiedź na podobne pytanie znajduje się również na stackoverflow.com/questions/47914325/...
HelenM,
1
Tekst tego podobnego pytania znajduje się teraz poniżej tej odpowiedzi. Zaczyna się od „FYI, parametr on_delete w modelach jest wstecz od tego, jak to brzmi”. Zapewnia znacznie więcej szczegółów niż oryginalne odpowiedzi.
HelenM,

Odpowiedzi:

783

Takie zachowanie należy zastosować po usunięciu obiektu, do którego istnieje odwołanie . Nie jest specyficzny dla django, jest to standard SQL.

W przypadku wystąpienia takiego zdarzenia można podjąć 6 działań:

  • CASCADE: Po usunięciu obiektu, do którego istnieje odwołanie, usuń także obiekty, które mają do niego odniesienia (np. Po usunięciu posta na blogu możesz również usunąć komentarze). SQL równoważne: CASCADE.
  • PROTECT: Zabroń usuwania obiektu, do którego istnieje odwołanie. Aby go usunąć, musisz usunąć wszystkie obiekty, które odnoszą się do niego ręcznie. SQL równoważne: RESTRICT.
  • SET_NULL: Ustaw odwołanie na NULL (wymaga, aby pole było nullable). Na przykład, gdy usuwasz użytkownika, możesz chcieć zachować komentarze, które opublikował na blogach, ale powiedz, że zostały opublikowane przez anonimowego (lub usuniętego) użytkownika. SQL równoważne: SET NULL.
  • SET_DEFAULT: Ustaw wartość domyślną. SQL równoważne: SET DEFAULT.
  • SET(...): Ustaw określoną wartość. Ten nie jest częścią standardu SQL i jest w całości obsługiwany przez Django.
  • DO_NOTHING: Prawdopodobnie bardzo zły pomysł, ponieważ spowodowałoby to problemy z integralnością w bazie danych (odwoływanie się do obiektu, który tak naprawdę nie istnieje). SQL równoważne: NO ACTION.

Źródło: dokumentacja Django

Zobacz także na przykład dokumentację PostGreSQL .

W większości przypadków CASCADEjest to oczekiwane zachowanie, ale dla każdego klucza zagranicznego należy zawsze zadać sobie pytanie, jakie jest oczekiwane zachowanie w tej sytuacji. PROTECTi SET_NULLczęsto są przydatne. Ustawienie CASCADEtam, gdzie nie powinno, może potencjalnie kasować całą bazę danych kaskadowo, po prostu usuwając pojedynczego użytkownika.


Dodatkowa uwaga wyjaśniająca kierunek kaskady

Zabawnie jest zauważyć, że CASCADEdla wielu osób kierunek działania nie jest jasny. Właściwie, to zabawne zauważyć, że tylkoCASCADE działanie nie jest jasne. Rozumiem, że zachowanie kaskady może być mylące, jednak musisz myśleć, że jest to ten sam kierunek, co każde inne działanie . Zatem jeśli uważasz, że ten CASCADEkierunek nie jest dla ciebie jasny, w rzeczywistości oznacza to, że on_deletezachowanie nie jest dla ciebie jasne.

W bazie danych klucz obcy jest zasadniczo reprezentowany przez pole liczby całkowitej, którego wartość jest kluczem podstawowym obiektu obcego. Załóżmy, że masz wpis comment_A , który ma obcy klucz do wpisu article_B . Jeśli usuniesz wpis komentarz_A , wszystko jest w porządku, artykuł_B żył bez komentarza_A i nie przejmuj się, jeśli zostanie usunięty. Jednakże, jeśli usuniesz article_B , następnie comment_A paniki! Nigdy nie żył bez article_B i potrzebuje go, jest częścią jego atrybutów ( article=article_Bale czym jest * article_B ** ???). Tutaj on_deletewkracza, aby ustalić, jak rozwiązać ten błąd integralności, albo mówiąc:

  • „Nie! Proszę! Nie! Nie mogę bez ciebie żyć!” (co jest powiedziane PROTECTw języku SQL)
  • „W porządku, jeśli nie jestem twój, to nie jestem nikim” (co jest powiedziane SET_NULL)
  • „Żegnaj świecie, nie mogę żyć bez article_B” i popełniać samobójstwo (takie jest CASCADEzachowanie).
  • „W porządku, mam zapasowego kochanka, od tej pory będę odwoływał się do article_C” ( SET_DEFAULTa nawet SET(...)).
  • „Nie mogę stawić czoła rzeczywistości, będę wołał twoje imię, nawet jeśli to mi pozostało!” ( DO_NOTHING)

Mam nadzieję, że dzięki temu kierunek kaskady będzie wyraźniejszy. :)

Antoine Pinsard
źródło
19
Głupie pytanie, ale kaskada powinna zawsze być jednokierunkowa, prawda? To znaczy, jeśli Comment ma klucz obcy, aby BlogPostusunąć BlogPost powinien usunąć komentarz, ale usunięcie komentarza nie powinno usunąć BlogPost, niezależnie od RDMS?
Anthony Manning-Franklin
20
@AnthonyManningFranklin Sure. Po usunięciu wyzwalane jest tylko wtedy, gdy odwołanie jest „zepsute”. Nie jest tak w przypadku usunięcia komentarza, ponieważ jednocześnie usuwasz odwołanie.
Antoine Pinsard
6
Pytanie nie jest głupie; Potrzebuję również tego wyjaśnienia. Zakładamy więc, że relacja jest jednostronna, właścicielem relacji jest Comment, kto ma pole FK w swojej tabeli, a BlogPost„jest właścicielem”, Commentjeśli mówimy o modelu z prawdziwego życia. Dobrze.
WesternGun
3
Ważną rzeczą do zapamiętania jest to, że ustawienie on_delete w Django NIE tworzy klauzuli ON DELETE w samej bazie danych. Określone zachowanie (takie jak CASCADE) będzie miało wpływ tylko na usuwanie wykonywane przez Django, a nie na usuwanie surowe wykonywane bezpośrednio w bazie danych.
JoeMjr2
2
Te cytaty na końcu wyglądają, jakby zostały wyciągnięte prosto z paneli komiksu Roya Lichtensteina! Niesamowite
nalot
42

Ta on_deletemetoda służy do poinformowania Django, co zrobić z instancjami modelu zależnymi od usuniętej instancji modelu. (np. ForeignKeyzwiązek). on_delete=models.CASCADEInformuje Django kaskadowy efekt kasowania tj kontynuować usuwanie modeli zależnych, jak również.

Oto bardziej konkretny przykład. Załóżmy, że masz Authormodel ForeignKeyw Bookmodelu. Teraz, jeśli usuniesz instancję Authormodelu, Django nie będzie wiedział, co zrobić z instancjami Bookmodelu zależnymi od tej instancji Authormodelu. Ta on_deletemetoda mówi Django, co należy zrobić w takim przypadku. Ustawienie on_delete=models.CASCADEinstruuje Django, aby kaskadowo usunąć efekt, tzn. Usunąć wszystkie Bookinstancje modelu, które zależą od Authorusuniętej instancji modelu.

Uwaga: on_deletestanie się wymaganym argumentem w Django 2.0. W starszych wersjach domyślnie jest to CASCADE.

Oto cała oficjalna dokumentacja.

him229
źródło
37

Do Twojej wiadomości, on_deleteparametr w modelach jest odwrócony od tego, jak to brzmi. Umieszczasz on_deletena modelu klucz obcy (FK), aby powiedzieć django, co zrobić, jeśli pozycja FK, na którą wskazujesz w swoim rekordzie, zostanie usunięta. Options nasz sklep zostały wykorzystane najbardziej są PROTECT, CASCADEi SET_NULL. Oto podstawowe zasady, które wymyśliłem:

  1. Użyj, PROTECTgdy twój FK wskazuje na tablicę przeglądową, która tak naprawdę nie powinna się zmieniać i która z pewnością nie powinna powodować zmiany tabeli. Jeśli ktoś spróbuje usunąć wpis z tej tabeli przeglądowej, PROTECTuniemożliwia mu usunięcie wpisu, jeśli jest on powiązany z dowolnymi rekordami. To także zapobiega django z usunięciem swój rekord tylko dlatego, że usunięty wpis w tabeli przeglądowej. Ta ostatnia część jest krytyczna. Gdyby ktoś usunął płeć „Kobieta” z mojej tabeli Płeć, NA PEWNO NIE chciałbym, aby natychmiast usunęła wszystkie osoby, które miałem w mojej Tabeli osób, które miały tę płeć.
  2. Użyj, CASCADEgdy twój FK wskazuje na rekord „nadrzędny”. Tak więc, jeśli Osoba może mieć wiele wpisów PersonEnicnicity (może być Indianinem, Czarnym i Białym), a ta Osoba zostanie usunięta, naprawdę chciałbym, aby wszelkie „Personalne” wpisy PersonEnicnicity zostały usunięte. Są bez znaczenia bez Osoby.
  3. Użyj, SET_NULLgdy chcesz , aby ludzie mogli usuwać wpis z tabeli przeglądowej, ale nadal chcesz zachować swój zapis. Na przykład, jeśli osoba może mieć liceum, ale tak naprawdę nie ma dla mnie znaczenia, czy liceum odejdzie na moim stole przeglądowym, powiedziałbym on_delete=SET_NULL. W ten sposób moja Osoba zostanie tam zapisana; po prostu ustawiłoby FK dla mojej szkoły na mojej osobie na zero. Oczywiście będziesz musiał pozwolić null=Truena to FK.

Oto przykład modelu, który wykonuje wszystkie trzy czynności:

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'

Jako ostatnią ciekawostkę, czy wiesz, że jeśli nie określisz on_delete(lub nie określisz ), zachowanie domyślne to CASCADE? Oznacza to, że jeśli ktoś usunie wpis dotyczący płci w tabeli Płeć, wszelkie rekordy Osoby tej płci również zostaną usunięte!

Powiedziałbym: „W razie wątpliwości ustaw on_delete=models.PROTECT”. Następnie przetestuj swoją aplikację. Szybko zorientujesz się, które FK powinny być oznaczone innymi wartościami, nie zagrażając żadnym z twoich danych.

Warto również zauważyć, że on_delete=CASCADEtak naprawdę nie jest dodawane do żadnej z migracji, jeśli takie zachowanie wybierzesz. Myślę, że dzieje się tak, ponieważ jest to ustawienie domyślne, więc stawianie on_delete=CASCADEjest tym samym, co stawianie niczego.

HelenM
źródło
12

Jak wspomniano wcześniej, CASCADE usunie rekord zawierający klucz obcy i odniesie się do innego obiektu, który został usunięty. Na przykład, jeśli masz witrynę z nieruchomościami i masz nieruchomość, która odwołuje się do miasta

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

a teraz, gdy miasto zostanie usunięte z bazy danych, wszystkie powiązane nieruchomości (np. nieruchomości znajdujące się w tym mieście) również zostaną usunięte z bazy danych

Teraz chcę również wspomnieć o zaletach innych opcji, takich jak SET_NULL lub SET_DEFAULT, a nawet DO_NOTHING. Zasadniczo z administracyjnego punktu widzenia chcesz „usunąć” te rekordy. Ale tak naprawdę nie chcesz, żeby zniknęły. Z wielu powodów. Ktoś mógł usunąć go przypadkowo lub w celu przeprowadzenia audytu i monitorowania. I proste raportowanie. Może to być sposób na „odłączenie” nieruchomości od miasta. Znowu będzie to zależeć od sposobu pisania aplikacji.

Na przykład niektóre aplikacje mają pole „usunięte”, które wynosi 0 lub 1. Wszystkie wyszukiwania i widoki list itp., Wszystko, co może pojawiać się w raportach lub gdziekolwiek użytkownik może uzyskać do nich dostęp z interfejsu użytkownika, wykluczają wszystko, co jest deleted == 1. Jeśli jednak utworzysz raport niestandardowy lub zapytanie niestandardowe, aby rozwinąć listę rekordów, które zostały usunięte, a nawet więcej, aby zobaczyć, kiedy była ostatnio modyfikowana (inne pole) i przez kogo (tj. Kto ją usunął i kiedy). jest to bardzo korzystne z wykonawczego punktu widzenia.

I nie zapominaj, że możesz przywrócić przypadkowe usunięcie tak proste, jak w deleted = 0przypadku tych rekordów.

Chodzi mi o to, że jeśli jest jakaś funkcjonalność, zawsze ma to jakiś powód. Nie zawsze jest to dobry powód. Ale powód. I często też dobry.

George Mohylewski
źródło
3
Było to pomocne, ponieważ wyjaśniło, w którym kierunku występuje CASCADE. Przyjęta odpowiedź nie jest jasna, jeśli nie znasz kaskad SQL.
codecribblr
Dziękuję :) bardzo mile widziane!
George Mohylewski,
2
Głosuję za odpowiedzią, ponieważ odpowiada ona na moje wątpliwości dotyczące kierunku w modelu relacji
edepe
6

Oto odpowiedź na pytanie, która brzmi: dlaczego używamy on_delete?

Po usunięciu obiektu, do którego odwołuje się ForeignKey, Django domyślnie emuluje zachowanie ograniczenia SQL NA USUŃ KASKADĘ, a także usuwa obiekt zawierający klucz zagraniczny. To zachowanie można zastąpić, podając argument on_delete. Na przykład, jeśli masz zerowalny klucz obcy i chcesz, aby był ustawiony na null po usunięciu obiektu odniesienia:

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

Możliwe wartości dla on_delete znajdują się w django.db.models:

KASKADA: Kaskada jest usuwana; domyślny.

PROTECT: Zapobiegaj usuwaniu obiektu, do którego istnieje odwołanie, podnosząc ProtectedError, podklasę django.db.IntegrityError.

SET_NULL: Ustaw wartość ForeignKey na wartość null; jest to możliwe tylko wtedy, gdy null ma wartość True.

SET_DEFAULT: Ustaw wartość klucza obcego na wartość domyślną; należy ustawić wartość domyślną dla klucza obcego.

Sonia Rani
źródło
Proste słowa wyjaśniają mi to, ponieważ nie jestem dojrzały także do sql i django. Dziękuję Ci.
wm.p1us
3

Załóżmy, że masz dwa modele, jeden o nazwie Osoba, a drugi o nazwie Firmy .

Z definicji jedna osoba może utworzyć więcej niż jedną firmę.

Biorąc pod uwagę, że firma może mieć jedną i tylko jedną osobę, chcemy, aby po usunięciu osoby usunąć wszystkie firmy powiązane z tą osobą.

Zaczynamy od utworzenia takiego modelu osoby

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

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

Wtedy model firm może wyglądać tak

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)

Zwróć uwagę na użycie on_delete=models.CASCADEw modelu Firmy. Oznacza to usunięcie wszystkich firm po usunięciu osoby, która jest jej właścicielem (instancja klasy Osoba).

Tiago Martins Peres 李大仁
źródło
1

Zmień orientację swojego modelu mentalnego na funkcjonalność „KASKADY”, myśląc o dodaniu FK do już istniejącej kaskady (np. Wodospadu). Źródłem tego wodospadu jest Klucz Podstawowy. Usuwa spływ.

Więc jeśli zdefiniujesz on_delete FK jako „CASCADE”, dodajesz ten rekord FK do kaskady usuwania pochodzących z PK. Rekord FK może uczestniczyć w tej kaskadzie lub nie („SET_NULL”). W rzeczywistości zapis z FK może nawet uniemożliwić przepływ operacji usuwania! Zbuduj tamę za pomocą „PROTECT”.

Gregory
źródło
0

Użycie KASKADY oznacza, że ​​faktycznie nakazuje Django, aby usunął odnośny rekord. W poniższym przykładzie aplikacji do ankiety: usunięcie „pytania” spowoduje również usunięcie opcji, które ma to pytanie.

np. pytanie: jak się o nas dowiedziałeś? (Wybory: 1. Znajomi 2. Reklama telewizyjna 3. Wyszukiwarka 4. Promocja e-mail)

Usunięcie tego pytania spowoduje również usunięcie wszystkich czterech opcji z tabeli. Zauważ, w którym kierunku płynie. Nie musisz umieszczać parametru on_delete = models.CASCADE w Modelu pytań umieść go w opcji Wybór.

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
źródło