2017-10-23 1 views
1

Ich habe Probleme beim Entfernen von Datensätzen aus einer has_many-Zuordnung in Rails, ohne unnötige Abfragen auszulösen. Im Wesentlichen habe ich ein Modell, das eine has_many-Beziehung hat, und ich möchte basierend auf einigen Kriterien mehrere Datensätze daraus entfernen. Ich möchte in der Lage sein, gleichzeitig die Verbindung auf dem neuesten Stand zu halten, aber auch die Datensätze aus der Datenbank zu entfernen, und dazu nur eine DELETE-Abfrage zu benötigen. Ich habe versucht, der Beziehung mit den neuen Objekten zuzuweisen (was unnötige UPDATE-Abfragen generiert) und delete_all aufruft (wodurch eine Abfrage erstellt wird, die Verknüpfung jedoch nicht aktualisiert wird).Entfernen von Datensätzen aus has_many Rails-Zuordnung mit einer einzigen Abfrage

+0

Rails müssen jedes Objekt suchen Sie löschen möchten die Rückrufe ('before_' laufen und' after_' destroy) auf jedem. Wenn Sie diesen Prozess, der ein rutschiger Hang ist, überspringen möchten, möchten Sie möglicherweise ein Serviceobjekt erstellen, das alle Logik über "alle verwandten Objekte löschen" enthält und die Zuordnung zurücksetzen – MrYoshiji

+0

Ich denke, das Zurücksetzen der Zuordnung ist auch nicht optimal, Denn wenn später auf die Assoziation zugegriffen wird, müssen die Objekte erneut abgerufen werden, obwohl wir wissen, was in der Relation sein sollte. – mp94

+0

IMO, sollten Sie nicht versuchen, die SQL-Abfragen zu reduzieren und das Verhalten von Rails neu zu definieren. Es kann zu verschiedenen Problemen führen, die auf den ersten Blick nicht sichtbar sind, aber viel später, wenn die ganze App sie seit Monaten benutzt ... – MrYoshiji

Antwort

0

Es gibt zwei Methoden zum Löschen von Datensätzen aus einer Beziehung. destroy löscht alle zugehörigen Datensätze nacheinander, wobei die Rückrufe für jeden gelöschten Datensatz ausgeführt werden. delete_all löscht alle Datensätze in einer Abfrage und führt die Rückrufe nicht aus.

in einem Modell So könnten Sie haben:

class Customer < ApplicationRecord 
    has_many :statements, dependent: :delete_all 

und wenn Sie den Kunden löschen

pry(main) foo = Customer.first 
pry(main)> foo.destroy 
    (0.3ms) BEGIN 
    SQL (290.4ms) DELETE FROM `statements` WHERE `statements`.`customer_id` = 3 
    SQL (2.6ms) DELETE FROM `customers` WHERE `customers`.`id` = 3 

Alles in einem Seegang foop getan, keine Rückrufe auf die gelöscht.

oder -

class Customer < ApplicationRecord 
    has_many :statements, dependent: :destroy 

und wenn Sie die Kunden löschen

pry(main) foo = Customer.first 
pry(main)> foo.destroy 
    (0.4ms) BEGIN 
    Statement Load (25.0ms) SELECT `statements`.* FROM `statements` WHERE `statements`.`customer_id` = 4 
    SQL (0.5ms) DELETE FROM `statements` WHERE `statements`.`id` = 9023 
    SQL (0.3ms) DELETE FROM `statements` WHERE `statements`.`id` = 9024 
    SQL (0.3ms) DELETE FROM `statements` WHERE `statements`.`id` = 9025 
    . . . etc etc etc . . . 

Jede abhängige Aufzeichnung eines nach dem anderen gelöscht, laufen Rückrufe auf jedem gelöscht.

Und delete_all und destroy kann auf jedem Ergebnissatz mit ähnlichen Auswirkungen ausgeführt werden. Also, wenn Sie wollte etwas Filter auf die zugehörigen Datensätze haben:

pry(main) foo = Customer.first 
pry(main)> bar = foo.statements.where(some_param: 42) 
pry(main)> bar.delete_all 
    SQL (4.2ms) DELETE FROM `statements` WHERE `statements`.`customer_id` = 7 AND `notices`.`some_param` = 42 
=> 2 

oder -

pry(main) foo = Customer.first 
pry(main)> bar = foo.statements.where(some_param: 42) 
pry(main)> bar.destroy_all 
    (0.2ms) BEGIN 
    SQL (0.6ms) DELETE FROM `notices` WHERE `notices`.`id` = 4639 
    (0.5ms) COMMIT 
    (0.2ms) BEGIN 
    SQL (0.5ms) DELETE FROM `notices` WHERE `notices`.`id` = 4640 
    (0.4ms) COMMIT 
    (0.1ms) BEGIN 
    SQL (0.5ms) DELETE FROM `notices` WHERE `notices`.`id` = 4641 
    (0.5ms) COMMIT 
    . . . etc etc etc . . . 
+0

Das Problem, das ich habe, ist das, wenn wir etwas wie 'foo.statements.where (some_param: 42) .delete_all' machen "foo.statements" ist nicht mehr das, was tatsächlich in der DB ist. – mp94

+0

In diesem Fall laden Sie die Anweisungen neu: 'foo.statements.reload' – rbb

Verwandte Themen