2014-04-11 5 views
9

Lassen Sie uns das einfach halten. Lassen Sie uns sagen, ich habe ein User Modell und ein Post Modell:Wie Sie Datensätze ausblenden, anstatt sie zu löschen (Soft Delete von Grund auf neu)

class User < ActiveRecord::Base 
    # id:integer name:string deleted:boolean 

    has_many :posts  
end 

class Post < ActiveRecord::Base 
    # id:integer user_id:integer content:string deleted:boolean 

    belongs_to :user 
end 

Nun, sagen wir mal ein Admin einen Beitrag zu „löschen“ (aus) will. Im Grunde genommen stellt er über das System das deleted-Attribut eines Beitrags auf 1. Wie soll ich diesen Beitrag nun in der Ansicht anzeigen? Sollte ich ein virtuelles Attribut auf der Post wie folgt aus:

class Post < ActiveRecord::Base 
    # id:integer user_id:integer content:string deleted:boolean 

    belongs_to :user 

    def administrated_content 
     if !self.deleted 
      self.content 
     else 
      "This post has been removed" 
     end 
    end 
end 

Während das funktionieren würde, möchte ich die oben in einer großen Anzahl von Modellen implementieren, und ich kann das Gefühl nicht los, dass die oben Copy + Einfügen Vergleichbar in allen meinen Modellen könnte DRYer sein. Viel Trockner.

Ich denke auch, eine deleted Spalte in jedem löschbaren Modell in meiner App zu setzen fühlt sich ein wenig umständlich. Ich denke, ich sollte eine "staatliche" Tabelle haben. Was sind Ihre Gedanken dazu:

class State 
    #id:integer #deleted:boolean #deleted_by:integer 

    belongs_to :user 
    belongs_to :post 
end 

und dann die Abfrage self.state.deleted im Komparator? Würde dies eine polymorphe Tabelle erfordern? Ich habe nur einmal versucht, polymorph zu sein und ich konnte es nicht zum Laufen bringen. (Es war auf einem ziemlich komplexen selbstreferentiellen Modell, Geist). Und das behebt immer noch nicht das Problem, eine sehr, sehr ähnliche Klassenmethode in meinen Modellen zu haben, um zu überprüfen, ob eine Instanz gelöscht wurde oder nicht, bevor Inhalt angezeigt wird.

Im deleted_by Attribut denke ich daran, die ID des Administrators zu platzieren, die es gelöscht hat. Aber was ist, wenn ein Admin einen Beitrag löscht? Vielleicht sollte ich nur eine edited_by ID haben.

Wie richte ich eine dependent: :destroy Art Beziehung zwischen dem Benutzer und seinen Posts ein? Denn jetzt möchte ich das tun: dependent: :set_deleted_to_0 und ich bin mir nicht sicher, wie das geht.

Auch wollen wir nicht einfach die gelöschten Attribute des Beitrags auf 1 setzen, weil wir eigentlich die Nachricht ändern wollen, die unsere administrated_content gibt. Wir wollen es jetzt sagen, This post has been removed because of its user has been deleted. Ich bin sicher, ich könnte einspringen und etwas Hackiges machen, aber ich möchte es von Anfang an richtig machen.

Ich versuche auch, Edelsteine ​​zu vermeiden, wenn ich kann, weil ich fühle, dass ich auf dem Lernen verpasse.

+0

Ich verwende normalerweise ein Feld "deleted_at", das ein Datetime-Feld ist. Wenn dieses Feld abgeschlossen ist, bedeutet dies, dass dieser Datensatz gelöscht wird (wirkt wie ein boolescher aber bietet ein Datum) – MrYoshiji

+2

Werfen Sie einen Blick auf Paranoia Edelstein -> https://github.com/radar/paranoia – usha

Antwort

13

Normalerweise verwende ich ein Feld deleted_at für diesen Fall genannt:

class Post < ActiveRecord::Base 
    scope :not_deleted, lambda { where(deleted_at: nil) } 
    scope :deleted, lambda { where("#{self.table_name}.deleted_at IS NOT NULL") } 

    def destroy 
    self.update_attributes(deleted_at: DateTime.current) 
    end 

    def delete 
    destroy 
    end 

    def deleted? 
    self.deleted_at.present? 
    end 
    # ... 

Wollen Sie diese functionnality zwischen mehreren Modellen teilen?

=> Machen Sie eine Erweiterung davon!

# lib/extensions/act_as_fake_deletable.rb 
module ActAsFakeDeletable 
    # override the model actions 
    def destroy 
    self.update_attributes(deleted_at: DateTime.current) 
    end 

    def delete 
    self.destroy 
    end 

    def undestroy # to "restore" the file 
    self.update_attributes(deleted_at: nil) 
    end 

    def undelete 
    self.undestroy 
    end 

    # define new scopes 
    def self.included(base) 
    base.class_eval do 
     scope :destroyed, where("#{self.table_name}.deleted_at IS NOT NULL") 
     scope :not_destroyed, where(deleted_at: nil) 
     scope :deleted, lambda { destroyed } 
     scope :not_deleted, lambda { not_destroyed } 
    end 
    end 
end 

class ActiveRecord::Base 
    def self.act_as_fake_deletable(options = {}) 
    alias_method :destroy!, :destroy 
    alias_method :delete!, :delete 
    include ActAsFakeDeletable 

    options = { field_to_hide: :content, message_to_show_instead: "This content has been deleted" }.merge!(options) 

    define_method options[:field_to_hide].to_sym do 
     return options[:message_to_show_instead] if self.deleted_at.present? 
     self.read_attribute options[:field_to_hide].to_sym 
    end 
    end 
end 

Verbrauch:

class Post < ActiveRecord::Base 
    act_as_fake_deletable 

Überschreiben der Standardwerte:

class Book < ActiveRecord::Base 
    act_as_fake_deletable field_to_hide: :title, message_to_show_instead: "This book has been deleted man, sorry!" 

Boom! Erledigt.

Warnung: Dieses Modul des destroy und delete Methoden des Activeüberschreibens, was bedeutet, dass Sie nicht in der Lage sein, Ihre Aufzeichnung zu zerstören mehr diese Methoden verwenden. Anstatt zu überschreiben, könnten Sie beispielsweise eine neue Methode namens soft_destroy erstellen. In Ihrer App (oder Konsole) würden Sie also soft_destroy verwenden, wenn dies relevant ist, und die Methoden destroy/delete verwenden, wenn Sie den Datensatz wirklich "hart zerstören" möchten.

+4

Wenn OP dies in einer Menge benötigt von Modellen wie in der Post impliziert, wäre ein Anliegen ein guter Ort dafür. –

+0

@MichaelKohl Ich möchte ein Beispiel dafür – Starkers

+0

Ich aktualisierte meine Antwort mit einem Modul @Starkers zum Beispiel – MrYoshiji

Verwandte Themen