2010-11-21 8 views
10

Ich habe eine Anwendung, wo ich das Verhalten von zerstören für viele meiner Modelle überschreiben möchte. Der Anwendungsfall besteht darin, dass Benutzer berechtigt sind, einen bestimmten Datensatz zu löschen, aber das Löschen der Zeile aus der Datenbank würde die referenzielle Integrität zerstören, die andere verwandte Modelle betrifft. Beispielsweise könnte ein Benutzer des Systems einen Kunden löschen wollen, mit dem er nicht mehr Geschäfte macht, aber Transaktionen mit diesem Kunden müssen gepflegt werden.Was ist die beste Methode, um das Verhalten von Rails ActiveRecord zu überschreiben?

Es scheint, ich habe mindestens zwei Möglichkeiten:

  1. Doppelte Daten in die notwendigerweise Modelle effektiv meine Datenmodell Denormalisierung, so dass gelöschte Datensätze nicht bezogenen Daten auswirken wird.
  2. Überschreiben Sie das "zerstören" Verhalten von ActiveRecord, um etwas zu tun, wie ein Flag setzen, das den Benutzer "löschte" den Datensatz angibt und dieses Flag verwendet, um den Datensatz auszublenden.

Fehle ich einen besseren Weg?

Option 1 scheint für mich eine schreckliche Idee zu sein, obwohl ich gerne Argumente für das Gegenteil hören würde.

Option 2 scheint etwas Rails-ish, aber ich frage mich, der beste Weg, damit umzugehen. Soll ich meine eigene Elternklasse erstellen, die von ActiveRecord :: Base erbt, überschreiben Sie dort die Destroy-Methode, und erben Sie dann von dieser Klasse in den Modellen, in denen ich dieses Verhalten möchte? Sollte ich das Suchverhalten auch außer Kraft setzen, werden die als gelöscht markierten Datensätze nicht standardmäßig zurückgegeben?

Wenn ich das mache, wie würde ich mit dynamischen Findern umgehen? Was ist mit benannten Bereichen?

Antwort

8

Wenn Sie nicht wirklich interessiert sind wieder die Datensätze zu sehen, sondern kümmert sich nur, dass die Kinder noch vorhanden sind, wenn die Eltern zerstört wird, ist die Aufgabe einfach: :dependent => :nullify zum has_many Gespräch noch Verweise auf NULL an die Mutter einzustellen automatisch bei der Zerstörung, und lehren Sie die Ansicht, damit umzugehen, dass diese Referenz fehlt. Dies funktioniert jedoch nur, wenn es Ihnen gut geht, die Zeile nie wieder zu sehen, d. H. Wenn Sie diese Transaktionen anzeigen, wird unter dem Firmennamen "[NO LONGER EXISTS]" angezeigt.

Wenn Sie tun wollen wieder, dass die Daten, um zu sehen, es klingt wie, was Sie wollen nichts mit tatsächlich zerstören Aufzeichnungen zu tun hat, was bedeutet, dass Sie nie wieder auf sie beziehen müssen. Verstecken scheint der richtige Weg zu sein.

Anstatt zu zerstören, da Sie nicht wirklich den Datensatz zerstören, scheint es wesentlich einfacher, Ihr Verhalten in eine hide Methode, die eine Flagge auslöst, wie Sie vorgeschlagen, zu setzen.

Wenn Sie diese Datensätze auflisten und nur sichtbare Datensätze einschließen möchten, besteht eine einfache Lösung darin, einen Bereich einzuschließen, der keine ausgeblendeten Datensätze enthält, und ihn nicht einzuschließen, wenn Sie diesen bestimmten, ausgeblendeten Bereich suchen möchten wieder aufnehmen. Ein anderer Pfad ist default_scope zu verstecken versteckten Datensätzen und Model.with_exclusive_scope { find(id) } zu verwenden, um einen versteckten Datensatz zu ziehen, aber ich würde dagegen empfehlen, da es eine ernsthafte Frage für einen eingehenden Entwickler sein könnte, und grundlegend ändert, was Model.all überhaupt nicht zurückgibt Reflektieren Sie, was der Methodenaufruf vorschlägt.

Ich verstehe den Wunsch, die Controller so aussehen zu lassen, als ob sie Dinge den Schienen-Weg tun, aber wenn Sie nicht Dinge tun, ist es am besten, explizit zu sein, vor allem, wenn es wirklich ist nicht so viel Schmerz dazu.

+0

Danke Matchu. Du hast Recht, ich brauche die Daten nicht mehr zu verwenden, daher ist das Nullen nicht das, wonach ich suche. Während die Einfachheit von Ryans Plugin, das in seiner Antwort erwähnt wird, ansprechend ist, überzeugte mich Ihr Argument dafür, dass ich über das Verhalten explizit bin. Ich schätze die Antwort. –

6

Ich schrieb a plugin for this exact purpose, Paranoia genannt. Ich "lieh" mir die Idee von acts_as_paranoid und schrieb AAP im Grunde viel weniger Code neu.

Wenn Sie destroy in einem Datensatz aufrufen, wird es nicht tatsächlich gelöscht. Stattdessen wird eine deleted_at Spalte in Ihrer Datenbank auf die aktuelle Uhrzeit gesetzt.

Die README auf der GitHub-Seite sollte für die Installation & Verwendung hilfreich sein. Wenn nicht, dann lass es mich wissen und ich werde sehen, ob ich das für dich reparieren kann.

Verwandte Themen