2014-04-11 88 views
9

讓我們保持這個簡單。比方說,我有一個User模型和Post型號:如何隱藏記錄,而不是刪除它們(從頭開始軟刪除)

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 

現在,讓我們說,一個管理員要「刪除」(隱藏)後。所以基本上,他通過系統將帖子的deleted屬性設置爲1。我現在應該如何在視圖中顯示這篇文章?我應該建立在後一個虛擬屬性是這樣的:

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 

雖然這會的工作,我想實現在大量的車型上面,我不禁感慨:複製粘貼+以上比較成我所有的模型可能是DRYer。很多烘乾機。

我也認爲在我的應用程序中將每個可刪除模型中的deleted列放在一起感覺有點麻煩。我覺得我應該有一個'狀態'表。什麼是對這個想法:

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

    belongs_to :user 
    belongs_to :post 
end 
在比較

,然後查詢self.state.deleted?這是否需要一個多態表?我只嘗試過一次多態,而我無法讓它工作。 (這是一個非常複雜的自我參照模型,頭腦)。這仍然沒有解決在我的模型中有非常非常類似的類方法在顯示內容之前檢查實例是否被刪除的問題。

deleted_by屬性中,我正在考慮放置刪除它的管理員標識。但是當管理員取消刪除帖子時呢?也許我應該只有一個edited_by ID。

如何在用戶和他的帖子之間設置dependent: :destroy類型的關係?因爲現在我想要做到這一點:dependent: :set_deleted_to_0,我不知道如何做到這一點。

此外,我們不只是想將帖子的刪除屬性設置爲1,因爲我們實際上想要更改我們的administrated_content發出的消息。我們現在想要說的是,This post has been removed because of its user has been deleted。我敢肯定,我可以跳進去做一些好事,但我想從一開始就做好。

我也儘量避免寶石,因爲我覺得我錯過了學習。

+0

我通常使用一個字段'deleted_at',它是一個日期時間字段。當這個字段完成時,這意味着這個記錄被刪除(像布爾值,但提供一個日期) – MrYoshiji

+2

看看偏執狂寶石 - > https://github.com/radar/paranoia – usha

回答

13

我通常使用的這種情況下命名爲deleted_at領域:

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 
    # ... 

想要分享多個模型之間的functionnality?

=>做一個擴展!

# 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 

用法:

class Post < ActiveRecord::Base 
    act_as_fake_deletable 

重寫默認值:

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

轟!完成。

警告:該模塊覆蓋的ActiveRecord的destroydelete方法,這意味着你將無法使用這些方法再摧毀你的記錄。例如,您可以創建一個名爲soft_destroy的新方法而不是覆蓋。因此,在您的應用程序(或控制檯)中,在相關時應使用soft_destroy,如果您確實要「硬銷燬」該記錄,請使用destroy/delete方法。

+4

如果OP需要這個在很多像這個帖子中暗示的模型,一個擔心會是一個很好的地方。 –

+0

@MichaelKohl我想要一個這樣的例子 – Starkers

+0

我用一個模塊@Starkers更新了我的答案 – MrYoshiji

相關問題