📅  最后修改于: 2023-12-03 15:19:41.970000             🧑  作者: Mango
在 Rails 应用程序中,有时需要从数据库中删除或破坏某些数据。这可能涉及到多个模型,与许多依赖关系。
在本文中,我们将讨论如何以一种有效和简单的方式处理这种情况。
在 Rails 中,有两种主要的方式来处理不再需要的数据:
在这篇文章中,我们将着重介绍如何使用 Rails 中的破坏方法。
要使某个模型支持破坏功能,首先需要向该模型添加一个布尔值列来表示该记录是否已被破坏。例如,如果您有一个名为 Event
的模型,并且希望支持软删除,则可以这样添加一个名为 deleted
的布尔列:
class AddDeletedToEvents < ActiveRecord::Migration[6.0]
def change
add_column :events, :deleted, :boolean, default: false
end
end
接下来,您需要定义一个 destroy
方法,以便它将记录标记为已刪除而不是从数据库中永久删除。对于 Event
模型,它可以像这样:
class Event < ApplicationRecord
def destroy
update(deleted: true)
end
end
现在,您可以在 Event
实例上调用 destroy
方法,而不会在数据库中删除记录。相反,它会标记为已刪除。
但是这种方法有一个问题:如果您有多个相互关联的模型,那么破坏一个模型的记录可能会影响与其相关的其他模型。例如,如果您有一个名为 User
的模型,还有一个名为 Post
的模型,其中 User
拥有很多 Post
,并且您在删除一个 User
记录时也想删除所有的 Post
记录,那该怎么办?
一种解决方案是使用 Rails 的 dependent: :destroy
选项来自动删除所有相关的记录。但是在软删除的情况下,我们仍需要标记这些相关的记录。
处理依赖关系
第一种解决方法是在父模型上覆盖 destroy
方法,然后逐个破坏子记录。例如,对于上面的 User
和 Post
的例子,可以这样:
class User < ApplicationRecord
has_many :posts, dependent: :destroy
def destroy
posts.each(&:destroy)
super
end
end
class Post < ApplicationRecord
def destroy
update(deleted: true)
end
end
这个 User
模型的 destroy
方法首先遍历它的所有 posts
,并将每个 post
都标记为已刪除。然后,它调用原始的 destroy
方法,这将标记 User
记录为已刪除。
这种方法的一个问题是对于每个相关的模型都要增加破坏的支持,这可能很繁琐。
使用 ActiveRecord 记录器
另一种更简单的方法是使用 ActiveRecord 记录器来记录模型之间的依赖关系。这是一个 Rails 内置功能,可用于记录 ActiveRecord 实例的更改。我们可以使用它来记录删除或软删除。
假设您有一个名为 Post
的模型,以及一个名为 Comment
的模型,其中 Post
有很多 comment
。您希望在软删除Post
时破坏所有 comment
。
首先,需要在 Post
中定义依赖关系:
class Post < ActiveRecord::Base
has_many :comments
has_many :destroyed_comments, -> { where(deleted: true) }, class_name: 'Comment', dependent: nil
end
Ok,接下来,我们利用 ActiveRecord 记录器来记录对 post
的更改。
class Post < ActiveRecord::Base
has_many :comments
has_many :destroyed_comments, -> { where(deleted: true) }, class_name: 'Comment', dependent: nil
around_destroy :record_destroyed_comments
private
def record_destroyed_comments
destroyed_associations = if destroyed?
# 如果 Post 已经被删除,我们不需要记录破坏的评论
[]
else
# 记录将破坏的评论
comments.select(&:should_destroy?)
end
yield
destroyed_associations.each(&:destroy)
end
end
我们在 Post
中定义了一个私有方法 record_destroyed_comments
,它记录应破坏的评论。我们将它作为 around_destroy
的回调来使用,以便在简历更改之前和之后运行它。在简历更改后,我们可以使用记录器来找到将要破坏的评论并摧毁它们。
在Comment
模型中,需要重写 destroy
方法。
class Comment < ActiveRecord::Base
belongs_to :post
scope :deleted, -> { where(deleted: true) }
def destroy
return super if deleted?
update(deleted: true)
end
def should_destroy?
deleted? || post.destroyed?
end
end
我们已经完成了处理依赖的方法,当我们调用 post.destroy
时,他会将自己软删除,同时破坏所有的 comment
。同时,我们还可以使用Bull的简单框架来执行定时任务删除所有已删除的软删除模型。
class CleanUp
include Bull::Job
cron '0 4 * * *'
def perform
[Event, User, Post, Comment].each do |model|
model.where(deleted: true).find_each(&:destroy)
end
end
end
本文介绍了在 Rails 应用程序中处理软删除的最佳实践。我们展示了如何使用记录器来处理模型之间的依赖,并提供了处理模型清理的基本工具。现在,您可以使用这些技巧来管理您的数据,从而实现更好的性能和安全性。