如何使用Ruby on Rails简化软删除过程?

时间:2012-10-05 06:03:16

标签: ruby-on-rails ruby ruby-on-rails-3

我希望有一个模型,我需要软删除一条记录,而不是在搜索中显示它们或在搜索时显示任何其他条件。

我想保留模型而不删除记录。如何解决这个问题?

11 个答案:

答案 0 :(得分:16)

试试这个gem:https://github.com/technoweenie/acts_as_paranoid - ActiveRecord插件,允许你隐藏和恢复记录而不实际删除它们

答案 1 :(得分:15)

只需在rails 4中使用一个问题

此处示例

    module SoftDeletable
  extend ActiveSupport::Concern


  included do
    default_scope { where(is_deleted: false) }
    scope :only_deleted, -> { unscope(where: :is_deleted).where(is_deleted: true) }
  end

  def delete
    update_column :is_deleted, true if has_attribute? :is_deleted
  end

  def destroy;
    callbacks_result = transaction do
      run_callbacks(:destroy) do
        delete
      end
    end
    callbacks_result ? self : false
  end

  def self.included(klazz)
    klazz.extend Callbacks
  end

  module Callbacks
    def self.extended(klazz)
      klazz.define_callbacks :restore
      klazz.define_singleton_method("before_restore") do |*args, &block|
        set_callback(:restore, :before, *args, &block)
      end
      klazz.define_singleton_method("around_restore") do |*args, &block|
        set_callback(:restore, :around, *args, &block)
      end
      klazz.define_singleton_method("after_restore") do |*args, &block|
        set_callback(:restore, :after, *args, &block)
      end
    end
  end

  def restore!(opts = {})
    self.class.transaction do
      run_callbacks(:restore) do
        update_column :is_deleted, false
        restore_associated_records if opts[:recursive]
      end
    end
    self
  end

  alias :restore :restore!

  def restore_associated_records
    destroyed_associations = self.class.reflect_on_all_associations.select do |association|
      association.options[:dependent] == :destroy
    end
    destroyed_associations.each do |association|
      association_data = send(association.name)
      unless association_data.nil?
        if association_data.is_deleted?
          if association.collection?
            association_data.only_deleted.each { |record| record.restore(recursive: true) }
          else
            association_data.restore(recursive: true)
          end
        end
      end
      if association_data.nil? && association.macro.to_s == 'has_one'
        association_class_name = association.options[:class_name].present? ? association.options[:class_name] : association.name.to_s.camelize
        association_foreign_key = association.options[:foreign_key].present? ? association.options[:foreign_key] : "#{self.class.name.to_s.underscore}_id"
        Object.const_get(association_class_name).only_deleted.where(association_foreign_key, self.id).first.try(:restore, recursive: true)
      end
    end
    clear_association_cache if destroyed_associations.present?
  end
end

可删除

轨道涉及添加软删除。

非常简单灵活的自定义/更改方式

(您可以将删除列更改为时间戳,并将方法更改为调用ActiveRecord touch)。

您想要控制代码的最佳位置没有用于简单任务的宝石。

用法

在你的表中添加一个布尔列is_deletable

class AddDeletedAtToUsers < ActiveRecord::Migration
  def change
    add_column :users, :is_deleted, :boolean
  end
end

在你的模特中

class User < ActiveRecord::Base
  has_many :user_details, dependent: :destroy

  include SoftDeletable
end

可用的方法和回调:

User.only_deleted
User.first.destroy
User.first.restore
User.first.restore(recursive: true)

注意: 焦点使用update_column或触摸,如果您决定使用时间戳列。

答案 2 :(得分:8)

只需添加一个名为deleted的布尔字段或其他内容即可。当您软删除记录时,只需将该字段设置为true。

执行查找时,只需将其添加为条件(或为其创建范围)。

答案 3 :(得分:5)

ActiveRecord 3中的default_scope功能使这很容易,但就个人而言,我赞成可以放入项目的wide variety标准解决方案。 acts_as_archive特别适合我的大多数项目,因为它将不经常访问的已删除记录移动到一个单独的表中,允许基表保持较小并且在数据库服务器的RAM中。

根据您的需要,您可能还需要考虑versioning而不是软删除。

答案 4 :(得分:4)

  1. 在模型中添加日期字段 - deleted_at。
  2. 覆盖模型上的删除(或销毁)方法以设置deleted_at值。您也可以将其创建为新方法。类似于soft_delete。
  3. 向模型添加restore / undelete方法,将deleted_at值设置为null。
  4. 可选:为原始删除(或销毁)方法创建别名方法。将其命名为hard_delete。

答案 5 :(得分:3)

您可以定义这样的模块

module ActiveRecordScope
  def self.included(base)
    base.scope :not_deleted, -> { base.where(deleted: false) }
    base.send(:default_scope) { base.not_deleted }
    base.scope :only_deleted, -> { base.unscope(where: :deleted).where(deleted: true) }

    def delete
      update deleted: true
    end

    def recover
      update deleted: false
    end
  end
end

然后在你的课堂上,你可以写下这样的内容:

class User < ActiveRecord::Base
  include ActiveRecordScope

end

所以你有软删除和恢复。

您致电user.delete以软删除用户。然后,您可以再次致电user.recover将已删除设置回 false ,然后将其恢复。

答案 6 :(得分:1)

看看rails3_acts_as_paranoid

  

一个简单的插件,隐藏记录而不是删除它们   能够恢复它们。

     

...

     

此插件的灵感来自acts_as_paranoidacts_as_active

<强>用法:

class Paranoiac < ActiveRecord::Base
    acts_as_paranoid
    scope :pretty, where(:pretty => true)
end

Paranoiac.create(:pretty => true)

Paranoiac.pretty.count #=> 1
Paranoiac.only_deleted.count #=> 0
Paranoiac.pretty.only_deleted.count #=> 0

Paranoiac.first.destroy

Paranoiac.pretty.count #=> 0
Paranoiac.only_deleted.count #=> 1
Paranoiac.pretty.only_deleted.count #=> 1

答案 7 :(得分:1)

如果你使用Rails4,试试这个gem:https://github.com/alfa-jpn/kakurenbo

Kakurenbo的联想功能优于其他宝石。

答案 8 :(得分:0)

如果我想获取所有记录,我不会使用默认范围原因,我需要使用“with_exclusive_scope”忽略它,而这反过来又是凌乱的。通过添加删除记录时设置的'已删除'布尔字段来实现。此外,还会添加范围以根据条件获取数据。

结帐Overriding a Rails default_scopeRails: Why is with_exclusive_scope protected? Any good practice on how to use it?

答案 9 :(得分:0)

在表中添加一列说状态,删除记录时将列状态的值更新为非活动状态。 在获取记录时,在查询中添加条件状态!=“非活动”。

答案 10 :(得分:0)

对于Rails 4,不要使用acts_as_paranoid(Rails 4的buggy),使用paranoia。您所要做的就是添加一个deleted_at时间戳列,并在模型中包含acts_as_paranoia。

从那里,只需在对象上调用destroy,所有其他ActiveRecord关系和大多数其他方法(如:count)自动排除soft_deleted记录。