How to hide records, rather than delete them (soft delete from scratch)

前端 未结 1 1861
滥情空心
滥情空心 2020-12-10 05:48

Let\'s keep this simple. Let\'s say I have a User model and a Post model:

class User < ActiveRecord::Base
    # id:integer name:         


        
相关标签:
1条回答
  • 2020-12-10 06:39

    I usually use a field named deleted_at for this case:

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

    Want to share this functionnality between multiple models?

    => Make an extension of it!

    # 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
    

    Usage:

    class Post < ActiveRecord::Base
      act_as_fake_deletable
    

    Overwriting the defaults:

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

    Boom! Done.

    Warning: This module overwrite the ActiveRecord's destroy and delete methods, which means you won't be able to destroy your record using those methods anymore. Instead of overwriting you could create a new method, named soft_destroy for example. So in your app (or console), you would use soft_destroy when relevant and use the destroy/delete methods when you really want to "hard destroy" the record.

    0 讨论(0)
提交回复
热议问题