In my app, I have several clients
, and they have several elements
(via has_many_through
association) depending on a certain BusinessType
to which Client
belongs to so that instead of manually adding all the elements
to the Client
, I can just select the BusinessType
and everything gets added automatically (business_type
in Client
is attr_readonly
). BusinessType
HABTM elements
.
Here's the catch, after creation with the default BusinessType
, the clients can update their elements and remove or add as they please (mostly add), so what I'm trying to do is the following:
Suppose one business_type
has elements [1,2,3]
and is assigned to one client
, then, the following elements are added manually to the client
= [4,5,6]
so it ends up having [1,2,3,4,5,6]
, ok everything's fine here.
But after this, the business_type
gets updated and has element 2
removed, so it ends up being [1,3]
. Here's the deal, I want the client to be updated by removing the 2
, but not the [4,5,6]
that do not correspond to the business_type
in question so that it ends up [1,3,4,5,6]
, I'm using an after_update
callback to update the clients'
elements but the _was
method doesn't work for HABTM relationships (to get the old business_type's
elements.
I've tried using a before_update
callback to first to client.elements = client.elements - business_type.elements
to store momentarily in the DB [1,2,3,4,5,6] - [1,2,3] = [4,5,6]
, and in the after_update do client.elements = client.elements + business_type.elements
to get [4,5,6] + [1,3] = [1,3,4,5,6]
but this has already the new value of [1,3]
. How can I get the old business_type.elements
value in the before_update
or after_update
?
Thanks in advance for your help!
I had a similar problem in an app, and the only solution I could come up with was to store the values before doing update_attributes in the controller.
Example code:
Models
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories, :join_table => "categories_products"
def remember_prev_values(values)
@prev_values = values
end
def before_update_do_something
puts @prev_values - self.category_ids # Any categories removed?
puts self.category_ids - @prev_values # Any categories added?
end
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :products, :join_table => "categories_products"
end
In the update method in the products controller I do the following:
class ProductsController < ApplicationController
...
def update
@product.remember_prev_values(@product.category_ids)
if @product.update_attributes(params[:product])
flash[:notice] = "Product was successfully updated."
redirect_to(product_path(@product))
else
render :action => "edit"
end
end
...
end
It is not ideal, but it is then possible to "catch" the habtm inserts/removes before they are executed.
I do think it is possible to do in a callback, but you might need to "hack" into ActiveRecord.
I did not spend much time on trying to dig into ActiveRecord internals, as this is a simple implementation that works.