Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 344
Swartz Avatar asked Dec 16 '10 21:12

Swartz


1 Answers

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.

like image 178
Swartz Avatar answered Oct 18 '22 20:10

Swartz