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
,我们要确保发生以下情况:
- 我们检查是否有其他
administrations
相同 calendar
。
- 如果有,我们什么都不做(用户只是被重定向到他的仪表板)。
- 但如果没有更多(因为用户是最后一个退出日历的用户),那么我们也想销毁日历。
- 由于我们的
Calendar
模型中的 has_many :posts, dependent: :destroy
行,这应该会自动销毁属于此日历的所有帖子,但我们认为检查它也不会有什么坏处。
我们正在考虑通过 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
模型中,您必须将 Calendar
和 Administration
绑定在一起。也许你发现此时此刻没问题,但真正的 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,一切都会按预期进行。
在我们的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
,我们要确保发生以下情况:
- 我们检查是否有其他
administrations
相同calendar
。 - 如果有,我们什么都不做(用户只是被重定向到他的仪表板)。
- 但如果没有更多(因为用户是最后一个退出日历的用户),那么我们也想销毁日历。
- 由于我们的
Calendar
模型中的has_many :posts, dependent: :destroy
行,这应该会自动销毁属于此日历的所有帖子,但我们认为检查它也不会有什么坏处。
我们正在考虑通过 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
模型中,您必须将 Calendar
和 Administration
绑定在一起。也许你发现此时此刻没问题,但真正的 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,一切都会按预期进行。