我想要一个模型,我需要软删除记录,而不是在搜索时在查找或任何其他条件中显示它们。
我想保留模型而不删除记录。如何解决这个问题?
我想要一个模型,我需要软删除记录,而不是在搜索时在查找或任何其他条件中显示它们。
我想保留模型而不删除记录。如何解决这个问题?
只需在 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
Rails 关注添加软删除。
非常简单灵活的定制/更改方式
(您可以将删除列更改为时间戳并更改调用 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 或触摸。
如果您使用的是 rails <= 3.x(此示例还使用 DateTime 字段而不是布尔值),则存在一些差异:
module SoftDeletable
extend ActiveSupport::Concern
included do
default_scope { where(deleted_at: nil }
# In Rails <= 3.x to use only_deleted, do something like 'data = Model.unscoped.only_deleted'
scope :only_deleted, -> { unscoped.where(table_name+'.deleted_at IS NOT NULL') }
end
def delete
update_column :deleted_at, DateTime.now if has_attribute? :deleted_at
end
# ... ... ...
# ... OTHERS IMPLEMENTATIONS ...
# ... ... ...
def restore!(opts = {})
self.class.transaction do
run_callbacks(:restore) do
# Remove default_scope. "UPDATE ... WHERE (deleted_at IS NULL)"
self.class.send(:unscoped) do
update_column :deleted_at, nil
restore_associated_records if opts[:recursive]
end
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.deleted_at?
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
在您的表格中添加一个 DateTime 列 deleted_at
class AddDeletedAtToUsers < ActiveRecord::Migration
def change
add_column :users, :deleted_at, :datetime
end
end
试试这个 gem:https ://github.com/technoweenie/acts_as_paranoid - ActiveRecord 插件允许您隐藏和恢复记录而不实际删除它们
只需添加一个名为的布尔字段deleted
或其他内容即可。当您软删除记录时,只需将该字段设置为 true。
在进行查找时,只需将其添加为条件(或为其设置范围)。
ActiveRecord 3 中的default_scope
功能使这很容易,但就个人而言,我更喜欢可以放入项目中的各种标准解决方案。acts_as_archive
特别是最适合我的大多数项目,因为它将不经常访问的已删除记录移动到单独的表中,从而允许基表保持较小并位于数据库服务器的 RAM 中。
根据您的需要,您可能还需要考虑版本控制而不是软删除。
您可以像这样定义一个模块
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
将deleted 重新设置为false,并恢复它。
一个简单的插件,可以隐藏记录而不是删除它们,能够恢复它们。
...
这个插件的灵感来自于acts_as_paranoid和acts_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
如果你使用 Rails4,试试这个 gem:https ://github.com/alfa-jpn/kakurenbo
Kakurenbo的联想功能比其他宝石要好。
如果我想获取所有记录,我不会使用默认范围,因为我需要使用“with_exclusive_scope”忽略它,这反过来又很混乱。这将通过添加一个“已删除”布尔字段来设置,该字段是在删除记录时设置的。此外,将添加范围以根据条件获取数据。
结帐覆盖 Rails default_scope和Rails:为什么 with_exclusive_scope 受保护?关于如何使用它的任何好的做法?
在表中添加一列说状态,并在删除记录时将列状态的值更新为非活动状态。并在获取记录时,在查询中添加条件状态!=“非活动”。
对于 Rails 4,不要使用acts_as_paranoid(Rails 4 的错误),使用paranoia。您所要做的就是添加一个deleted_at 时间戳列并在模型中包含acts_as_paranoia。
从那里,只需调用destroy
对象和所有其他 ActiveRecord 关系和大多数其他方法(如:count)自动排除 soft_deleted 记录。