HMT collection_singular_ids= deletion of join models is direct, no destroy callbacks are triggered

不想你离开。 提交于 2019-12-20 14:23:58

问题


Just ran into an issue with a has_many :through association and after/before-destroy callbacks not being triggered.

Say, I have users, groups, and an intermediate relation called membership. I have a form that allows users to be enrolled into groups by creating a new membership record when they check off associated checkboxes. Basically an array of group_ids.

Looks something like this:

Which group would you like to join? (check all that apply)
[] Group A
[] Group B
[] Group C

And I wish to record actions such as joining a group or leaving a group to activity log table and do some other less important thigns.

I have the following defined:

class Group < AR::Base
  has_many :memberships
  has_many :users, :through => :memberships
end

class Membership < AR::Base
  belongs_to :user
  belongs_to :group

  after_create :log_event_to_audit_table
  after_destroy :log_event_to_audit_table

end

class User < ActiveRecord::Base
  has_many :memberships
  has_many :groups, :through => :memberships

  attr_accessible :group_ids   # enables mass-assignment
end

When a new membership record is created the after_create is run as expected. However, the after_destroy does not get triggered!

After google-ing and read up the docs I discovered the reason why:

"Automatic deletion of join models is direct, no destroy callbacks are triggered" - from Ruby Guides.

Hmmmmmm...

So the join model's (in this case Membership's) destroy callbacks are not being triggered. Well that's a downer. Any reason as to why?

So my question what is the best way to work around this issue? Should I define my own membership_ids= method in User model that calls membership.destroy directly?

Open to any suggestions about the best practices in such a scenario.

Thanks!


回答1:


After carefully examining the API docs, it turns out has_many and has_and_belongs_to_many ("HABTM") have a few options just for this case:

  • before_add
  • after_add
  • before_remove
  • after_remove

class User < ActiveRecord::Base
  has_many :groups, :through => :memberships, :after_remove => :your_custom_method
end

Judging by how many responses I got, this must not be a very well documented/used feature.

Just noting it here for myself and others who may stumble like I did.




回答2:


I've struggled with the same problem recently and solved it by extending association and overriding its delete method:

class User < ActiveRecord::Base
  has_many :memberships
  has_many :groups, :through => :memberships do
    def delete(*args)
      groups = args.flatten
      # destroy memberships in order to trigger their callbacks:
      proxy_association.owner.memberships.where(group_id: groups).destroy_all
      super
    end
  end
  ...
end



回答3:


adding dependent: :destroy to the has many relationship actually calls the before_destroy and after_destroy methods in the Membership class.

class User < ActiveRecord::Base
  has_many :groups, through: :memberships, dependent: :destroy
end


来源:https://stackoverflow.com/questions/4465607/hmt-collection-singular-ids-deletion-of-join-models-is-direct-no-destroy-callb

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!