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

Jey*_*han 13 ruby ruby-on-rails ruby-on-rails-3

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

我想保留模型而不删除记录.怎么去这个?

Abs*_*Abs 20

只需在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
Run Code Online (Sandbox Code Playgroud)

可删除

轨道涉及添加软删除.

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

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

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

用法

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

class AddDeletedAtToUsers < ActiveRecord::Migration
  def change
    add_column :users, :is_deleted, :boolean
  end
end
Run Code Online (Sandbox Code Playgroud)

在你的模特

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

  include SoftDeletable
end
Run Code Online (Sandbox Code Playgroud)

可用的方法和回调:

User.only_deleted
User.first.destroy
User.first.restore
User.first.restore(recursive: true)
Run Code Online (Sandbox Code Playgroud)

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


小智 17

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

  • 当我们可以使用Rails问题执行单个文件任务时,我总是避免使用宝石. (5认同)
  • @Abs 你能详细说明为什么吗?无论文件数量如何,重新实现功能的原因都不是很明显...... (2认同)
  • 这将是另一个依赖项,但至少它是一个经过测试的依赖项。 (2认同)

sos*_*orn 9

只需添加一个名为deleted或类似的布尔字段即可.当您软删除记录时,只需将该字段设置为true即可.

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


wil*_*ynn 5

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

根据您的需要,您可能还需要考虑版本控制而不是软删除.