Czy mogę ustawić usuwanie kaskadowe w Railsach?

88

Wiem, że prawdopodobnie jest to gdzieś w Internecie, ale nie mogę znaleźć odpowiedzi tutaj w Stackoverflow, więc pomyślałem, że mogę tutaj trochę zwiększyć bazę wiedzy.

Jestem nowicjuszem w Ruby and Rails, ale moja firma jest w to dość mocno zainwestowana, więc staram się to poznać bardziej szczegółowo.

Trudno mi było zmienić sposób myślenia na projektowanie aplikacji z „modelu”, a nie z bazy danych, więc próbuję dowiedzieć się, jak wykonać całą pracę projektową, którą klasycznie wykonałem w bazie danych w Zamiast tego model szynowy.

Więc ostatnim zadaniem, które sobie postawiłem, jest dowiedzieć się, jak skonfigurować model bazy danych Rails, aby wykonywać usuwanie kaskadowe? Czy jest na to łatwy sposób? A może musiałbym wejść do MySql i to ustawić?

matt_dev
źródło

Odpowiedzi:

103

możesz również ustawić opcję: depend na: delete_all. : delete_all wyda jedną instrukcję SQL, aby usunąć wszystkie rekordy potomne. z tego powodu użycie: delete_all może dać lepszą wydajność.

has_many :memberships, dependent: :delete_all
Mike Breen
źródło
8
Twoje wyjaśnienie jest mylące. Zostanie użyta pojedyncza instrukcja SQL, ale metoda zniszczenia nie zostanie wywołana dla każdego wiersza podrzędnego. W tym celu musisz użyć Destroy_all.
John Topley
@John - mam nadzieję, że zmiany wyjaśnią zamieszanie. dzięki za wskazanie tego.
Mike Breen
26
Upewnij się, że rozumiesz różnicę między użyciem :delete_alla :destroytym. Oba spowodują, że członkostwa podrzędne (1 poziom do usuwania [potrzebne cytowanie] i ndo niszczenia (jeśli ich dzieci mają niszczenia zależne)) zostaną usunięte z bazy danych, ale :destroyutworzą instancję każdego obiektu podrzędnego i najpierw uruchomią wszelkie wywołania zwrotne, podczas gdy :delete_allbezpośrednio uruchomią Instrukcja SQL DELETE w bazie danych. :destroyjest z tego powodu wolniejszy, ale umożliwia wywołanie zwrotne, gdy rekord zostanie zniszczony. Omijanie Railsów na jednym końcu i potencjalna instancja n ^ x na drugim.
jstim,
2
Proponuję również skonfigurować klucze obce bazy danych. W ten sposób rekordy są usuwane za pomocą jednej operacji. Zobacz poniższą odpowiedź, którą opublikowałem.
Hendrik,
66

Tak, możesz, jeśli używasz relacji takiej jak has_many, po prostu to zrób

has_many :memberships, dependent: :destroy
danmayer
źródło
Dan, więc wydaje mi się, że moje następne pytanie brzmi: czy jeśli uruchomię polecenie migracji db, czy to faktycznie ustawi to w bazie danych? A może kaskadowanie jest całkowicie obsługiwane przez szyny?
matt_dev
Tak, jest obsługiwany przez szyny. (Upewnij się jednak, że naprawdę zawsze musisz usunąć wszystkie powiązane wiersze.)
Stein G. Strindhaug,
@Matt - linia has_many powinna znajdować się w Twojej klasie modelu, migracja tego nie doda.
Gareth
Wolę to rozwiązanie, ponieważ działa również, jeśli model zależny ma inną relację has_many
tpei
25

W przeciwieństwie do udzielonej odpowiedzi, zdecydowanie sugeruję zrobienie tego również na poziomie bazy danych. Jeśli masz różne procesy lub środowisko wielowątkowe, może się zdarzyć, że rekordy nie zostaną poprawnie usunięte. Ponadto klucz obcy bazy danych znacznie przyspiesza usuwanie dużej ilości danych.

Jak w sugerowanej odpowiedzi zrób to:

has_many :memberships, dependent: :delete_all

Pamiętaj jednak, aby skonfigurować plik foreign_keyw migracji. W ten sposób baza danych automatycznie usuwa rekordy za Ciebie.

Aby anulować wartości po usunięciu członkostwa, zakładając, że masz model użytkownika:

add_foreign_key :users, :memberships, on_delete: :nullify

Możesz również usunąć wszystkie modele po usunięciu członkostwa

add_foreign_key :users, :memberships, on_delete: :cascade
Hendrik
źródło
Czy mogę więc używać zarówno „has_many: członkostwa, zależne:: usuń_all”, jak i „add_foreign_key: użytkownicy,: członkostwa, on_delete:: cascade”? Czy to zadziała dobrze?
Rubycon
2
Nie musisz nawet konfigurować delete_allw modelu. Klucz obcy zadba o prawidłowe usunięcie wszystkiego na poziomie bazy danych.
Hendrik
3
Ciekawi mnie, co się stanie, gdy zrobisz jedno i drugie. Wygląda na to, że nie powinno to mieć negatywnego wpływu, ale czy ktoś miał złe doświadczenia z tą praktyką robienia zarówno na poziomie AR, jak i DB?
James Klein
1
Poziom bazy danych jest tym, czego szukałem. Moim zdaniem powinna to być akceptowana odpowiedź. Inne wydają się działać tylko wtedy, gdy moje zapytania trzymają się standardowych operacji ActiveRecord.
Brett Beatty
10

Pamiętaj tylko, że delete_all nie wykona żadnych wywołań zwrotnych (takich jak before_destroy i after_destroy) na rekordach podrzędnych.

Jarin Udom
źródło
6

Wygląda na to, że ta wtyczka może dać ci to, czego szukasz, jeśli chcesz, aby usuwanie kaskadowe było odzwierciedlone w rzeczywistej strukturze bazy danych:

http://www.redhillonrails.org/foreign_key_migrations.html

Format użycia tego podczas migracji wyglądałby tak:

create_table :orders do |t|
  t.column :customer_id, :integer, :on_delete => :set_null, :on_update => :cascade
  ...
end
Sean McMains
źródło
5
Ten link jest martwy, ale jest to nowsza alternatywa: github.com/matthuhiggins/foreigner
gdelfino.