在销毁关联对象时删除N + 1查询

时间:2018-05-15 21:46:56

标签: sql ruby-on-rails

我正在创建一个Blog应用程序,我正在尝试在用户控制器中编写destroy方法来处理用户被销毁时的情况。我希望它触发删除所有相关对象的操作,但是当我指定"依赖:: destroy"在模型中,我得到N + 1个查询(或N * M,如果那可能......) - 每一篇文章都被逐一删除,然后是所有评论,所有标签,所有标签(表格到处理HABTM关系)。

我曾尝试编写服务对象以摆脱这个长查询,但我无法使其工作,因为我违反了外键约束"。 这里有什么可能性?我不想在我的数据库中允许nils只是为了能够通过运行在DB中查找nils的查询来轻松清理数据库(以避免用户输入奇怪的内容),因此依赖:: nullify赢了在这里工作......如果可能的话,我也不愿使用Callbacks。

在选择带有服务的解决方案时,我首先等待用户被删除,然后调用服务来清理Orhpans,但是当代码执行错误时,我最终会遇到nils - 我应该使用交易吗?在这?

用户可以是文章的作者,该文章被标记,可以被其他用户评论。

以下是主要课程:

用户:

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
        :recoverable, :rememberable, :trackable, :validatable

  has_many :articles, foreign_key: 'author_id'
  has_many :comments, foreign_key: 'author_id'
  validates :email, presence: true
end

文章:

class Article < ApplicationRecord
  include ActiveModel::Validations
  belongs_to :author, class_name: 'User'
  has_many :taggings
  has_many :tags, through: :taggings
  has_many :comments
end

注释:

class Comment < ApplicationRecord
  belongs_to :author, class_name: 'User'
  belongs_to :article
  belongs_to :parent, class_name: 'Comment', optional: true
  has_many :children, class_name: 'Comment', foreign_key: 'parent_id', dependent: :delete_all
  acts_as_tree order: 'created_at ASC'
end

标签:

class Tag < ApplicationRecord
  has_many :taggings
  has_many :articles, through: :taggings
end

标记:

class Tagging < ApplicationRecord
  belongs_to :tag
  belongs_to :article
end

1 个答案:

答案 0 :(得分:2)

这是预期的行为。您有几个选择:

1)您可以创建一个服务对象,通过查询手动完成所有删除操作,从您最远的关系开始。例如:

user = User.find(id_of_user_to_destroy)

user.articles.each do |article|
  article = Article.find(id_of_article_to_destroy)
  Tag.where(articles: [article]).delete_all #delete_all skips callbacks
  Tagging.where(article: article).delete_all
  Comment.where(...).delete_all
  article.destroy
end

user.destroy

这通常表现更好,但难以维持。

2)以某种方式将用户标记为“已删除”(布尔字段),然后在后台作业中调用User.destroy。您仍将拥有大量的数据库查询,但使用本机AR dependency属性进行维护会更容易。