3 models: User, Movie, Like
User has_many :likes
User has_many :movies, through: :likes
This code:
user.movies = [ m1, m2, m3 ]
calls Like#after_create
for the new Like
records that relate user and m1/m2/m3. Then, this code:
user.movies = [ m3, m4 ]
doesn't call Like#after_destroy
for the Like
records that relate user to m1/m2, but does call Like#after_create
for the new relationship with m4.
The movies
collection could be set manually or with a form that has user[movie_ids]
checkboxes, and then
user.update_attributes(params[:user])
- What is the right Rails approach for setting a collection?
- How do I force it to call after_destroy?
Update:
As @jdoe cited from the docs, it's not achievable when assigning new collection or when deleting from a collection (user.movies.delete(m1)
). The only way is to use before_remove
/after_remove
callbacks on the user model (and in case of a polymorphic relationship -- any other model), with the has_many
definition:
has_many :movies, through: :likes, before_remove: :before_like_destroy, after_remove: after_like_destroy
def before_like_destroy(movie)
like = self.likes.where(movie_id: movie)
# trigger the after_destroy on like
like.trigger_before_destroy # to be implemented on like, should call what the original callbacks contained
end
def after_like_destroy(movie)
# no way to get Like object here because it was already removed, need to remember it on the before_destroy somehow
end
Can't understand the logic behind it. It makes callbacks on relationship models totally useless. If something happened on after_create
it can't be undone in after_destroy
, and since it's better practice to have after and before logic together and not separately, it makes all callbacks useless.
Think I'm gonna write a gem that does that automagically.