Rails 4 has_many :through relationship: 当子模型实例计数达到0时销毁父模型实例

Rails 4 has_many :through relationship: destroy parent model instance when child model instance count reaches 0

在我们的Rails4 app中,有四种模式:

class User < ActiveRecord::Base
  has_many :administrations, dependent: :destroy
  has_many :calendars, through: :administrations
end

class Administration < ActiveRecord::Base
  belongs_to :user
  belongs_to :calendar
end

class Calendar < ActiveRecord::Base
  has_many :administrations, dependent: :destroy
  has_many :users, through: :administrations
  has_many :posts, dependent: :destroy
end

class Post < ActiveRecord::Base
    belongs_to :calendar
end

以下是相应的迁移:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :first_name
      t.string :last_name
      t.string :email
      t.integer :total_calendar_count
      t.integer :owned_calendar_count

      t.timestamps null: false
    end
  end
end

class CreateAdministrations < ActiveRecord::Migration
  def change
    create_table :administrations do |t|
      t.references :user, index: true, foreign_key: true
      t.references :calendar, index: true, foreign_key: true
      t.string :role

      t.timestamps null: false
    end
  end
end

class CreateCalendars < ActiveRecord::Migration
  def change
    create_table :calendars do |t|
      t.string :name

      t.timestamps null: false
    end
  end
end

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
        t.references :calendar, index: true, foreign_key: true
        t.date :date
        t.time :time
        t.string :focus
        t.string :format
        t.string :blog_title
        t.text :long_copy
        t.text :short_copy
        t.string :link
        t.string :hashtag
        t.string :media
        t.float :promotion
        t.string :target
        t.integer :approval
        t.text :comment

      t.timestamps null: false
    end
  end
end

每次 user 退出 calendar,这意味着我们 destroy 相应的 administration,我们要确保发生以下情况:

我们正在考虑通过 Calendar 模型中的 private clear_calendar 方法来实现这一点,我们将在 [=26] 中用作 after_destroy 回调=] 控制器:

private

  def clear_calendar
    @calendar = Calendar.find(params[:id])
    unless @calendar.administration.exists?
      @calendar.destroy
    end
  end

这有意义吗?

这是一个非常明智的方法,全世界都为您没有将此逻辑放在控制器操作中而感到高兴。

需要注意的一件事:通过将此逻辑放入您的 Calendar 模型中,您必须将 CalendarAdministration 绑定在一起。也许你发现此时此刻没问题,但真正的 object-oriented 程序不会询问另一个模型是否存在,而是告诉模型 它想要什么 ,而不是 how 它希望它完成(例如,如果此关联不存在,则删除 self)。

我建议将此逻辑放在一个 PORO 中——也许是一个服务——从你的 database-backed objects 中移除不必要的耦合。也许看起来像这样:

class Calendar < ActiveRecord::Base

...

private

  def clear_calendar
    ParentDestructionService.new(self)
  end

end


class ParentDestructionService
  def initialize(parent)
    @parent = parent
  end

  .....logic goes here.....
end

这样,您不仅可以真正将 how 过程与不应该关心 Administration 的 class 过程分开,而且您现在拥有了将其粘贴在 Sidekiq 进程中或简单地将其线程化的能力。无论哪种方式,您都更加灵活。现在,在未来,您将能够通过该服务发送任何 parent,一切都会按预期进行。