Kann ich das Löschen von Kaskaden in Rails einrichten?

88

Ich weiß, dass dies wahrscheinlich irgendwo im Internet ist, aber ich kann die Antwort hier auf Stackoverflow nicht finden, also dachte ich, ich könnte die Wissensbasis hier ein wenig erweitern.

Ich bin ein Neuling bei Ruby and Rails, aber meine Firma investiert ziemlich viel in sie und ich versuche, sie etwas genauer kennenzulernen.

Es war schwierig für mich, meine Einstellung dahingehend zu ändern, dass eine Anwendung aus dem "Modell" und nicht aus der Datenbank entworfen wurde. Daher versuche ich herauszufinden, wie die gesamte Entwurfsarbeit, die ich klassisch in der Datenbank in der Datenbank ausgeführt habe, ausgeführt werden würde Schienenmodell stattdessen.

Die letzte Aufgabe, die ich mir gestellt habe, besteht darin, herauszufinden, wie ein Rails-Datenbankmodell für kaskadierende Löschvorgänge konfiguriert werden kann. Gibt es eine einfache Möglichkeit, dies zu tun? Oder müsste ich in MySql gehen und dies einrichten?

matt_dev
quelle

Antworten:

103

Sie können die Option: abhängig auch auf: delete_all setzen. : delete_all gibt eine einzelne SQL-Anweisung aus, um alle untergeordneten Datensätze zu löschen. Aus diesem Grund kann die Verwendung von: delete_all zu einer besseren Leistung führen.

has_many :memberships, dependent: :delete_all
Mike Breen
quelle
8
Ihre Erklärung ist verwirrend. Es wird eine einzelne SQL-Anweisung verwendet, aber die Zerstörungsmethode wird nicht für jede untergeordnete Zeile aufgerufen. Dafür müssen Sie destroy_all verwenden.
John Topley
@ John - hoffe, die Änderungen klären die Verwirrung. Vielen Dank für den Hinweis.
Mike Breen
25
Stellen Sie sicher, dass Sie den Unterschied zwischen :delete_allund dafür verstehen :destroy. Beides führt dazu, dass untergeordnete Mitgliedschaften (1 Ebene zum Löschen [Zitieren erforderlich] und nzum Zerstören (wenn ihre Kinder abhängige Zerstörungen haben)) aus der Datenbank entfernt :destroywerden, instanziieren jedoch jedes untergeordnete Objekt und führen zuerst alle Rückrufe aus, während a :delete_alldirekt ausgeführt wird SQL DELETE-Anweisung in der Datenbank. :destroyist aus diesem Grund langsamer, ermöglicht jedoch Rückrufe, wenn ein Datensatz zerstört wird. Umgehung von Schienen an einem Ende und mögliche n ^ x Instanziierung am anderen Ende.
jstim
2
Ich schlage vor, auch Datenbank-Fremdschlüssel einzurichten. Auf diese Weise werden Datensätze mit einer Operation gelöscht. Siehe die folgende Antwort, die ich gepostet habe.
Hendrik
66

Ja, Sie können, wenn Sie eine Beziehung wie has_many verwenden, tun Sie dies einfach

has_many :memberships, dependent: :destroy
danmayer
quelle
Dan, also denke ich, meine nächste Frage ist, ob ich einen db-Migrationsbefehl ausführe, der das tatsächlich in der Datenbank einrichtet. Oder wird die Kaskadierung komplett von Schienen gehandhabt?
matt_dev
Ja, es wird von Schienen gehandhabt. (
Stellen
@Matt - Die Zeile has_many sollte sich in Ihrer Modellklasse befinden. Die Migration fügt dies für Sie nicht hinzu.
Gareth
Ich bevorzuge diese Lösung, weil sie auch funktioniert, wenn das abhängige Modell eine andere has_many-Beziehung hat
tpei
25

Im Gegensatz zu der bereitgestellten Antwort empfehle ich dringend, dies auch auf Datenbankebene zu tun. Wenn Sie unterschiedliche Prozesse oder eine Umgebung mit mehreren Threads haben, kann es vorkommen, dass Datensätze nicht ordnungsgemäß gelöscht werden. Darüber hinaus beschleunigt der Datenbank-Fremdschlüssel das Löschen vieler Daten erheblich.

Wie in der vorgeschlagenen Antwort tun Sie dies:

has_many :memberships, dependent: :delete_all

Stellen Sie jedoch auch sicher, dass Sie eine foreign_keyin einer Migration einrichten. Auf diese Weise sorgt die Datenbank dafür, dass die Datensätze automatisch für Sie gelöscht werden.

So annullieren Sie die Werte, wenn eine Mitgliedschaft gelöscht wird, vorausgesetzt, Sie haben ein Benutzermodell:

add_foreign_key :users, :memberships, on_delete: :nullify

Sie können auch alle Modelle löschen, wenn eine Mitgliedschaft gelöscht wird

add_foreign_key :users, :memberships, on_delete: :cascade
Hendrik
quelle
Kann ich also sowohl "has_many: mitgliedschaften, abhängig :: delete_all" als auch "add_foreign_key: users ,: mitgliedschaften, on_delete :: cascade" verwenden? Wird es gut funktionieren?
Rubycon
2
Sie müssen das nicht einmal delete_allim Modell einrichten . Der Fremdschlüssel sorgt dafür, dass auf Datenbankebene alles für Sie ordnungsgemäß gelöscht wird.
Hendrik
3
Ich bin gespannt, was passiert, wenn Sie beides tun. Scheint, als sollte es keinen negativen Effekt haben, aber hat jemand schlechte Erfahrungen mit dieser Praxis gemacht, sowohl AR- als auch DB-Level zu machen?
James Klein
1
Die Datenbankebene ist das, wonach ich gesucht habe. Dies sollte meiner Meinung nach die akzeptierte Antwort sein. Die anderen scheinen nur zu funktionieren, wenn sich meine Abfragen an Standard-ActiveRecord-Vorgänge halten.
Brett Beatty
10

Denken Sie daran, dass delete_all keine Rückrufe (wie before_destroy und after_destroy) für die untergeordneten Datensätze ausführt.

Jarin Udom
quelle
6

Es sieht so aus, als ob dieses Plugin Ihnen das bietet, wonach Sie suchen, wenn Sie möchten, dass sich die kaskadierenden Löschvorgänge in der tatsächlichen Datenbankstruktur widerspiegeln:

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

Das Format für die Verwendung in einer Migration wäre ungefähr so:

create_table :orders do |t|
  t.column :customer_id, :integer, :on_delete => :set_null, :on_update => :cascade
  ...
end
Sean McMains
quelle
5
Dieser Link ist tot, aber dies ist eine neuere Alternative: github.com/matthuhiggins/foreigner
gdelfino