They way I see it is that you don't have a many to many relationship to the products. You have it to the product of that particulary variety (if any). So in order to keep track of it, I would use an extra model. That way you would be able to keep the relationship between products and varieties as well.
The models:
class User < ActiveRecord::Base
has_many :user_products, :dependent => :destroy
has_many :products, :through => :user_products
has_many :varieties, :through => :user_products
end
class UserProduct < ActiveRecord::Base
belongs_to :user
belongs_to :product
belongs_to :variety
end
class Product < ActiveRecord::Base
has_many :varieties
end
class Variety < ActiveRecord::Base
belongs_to :product
end
The controller:
class UserProductsController < ApplicationController
before_filter do
@user = User.find(params['user_id'])
end
def create
product = Product.find(params['product_id'])
variety = Variety.find(params['variety_id']) if params['variety_id']
@user_product = UserProduct.new
@user_product.user = user
@user_product.product = product
@user_product.variety = variety
@user_product.save
end
def destroy
# Either do something like this:
conditions = {:user_id => @user.id}
conditions[:product_id] = params[:product_id] if params[:product_id]
conditions[:variety_id] = params[:variety_id] if params[:variety_id]
UserProduct.destroy_all conditions
end
end
The views: If you are not interested in grouping the varieties into the different products and just put them as a list it is enough with this:
# users/show.html.erb
<%= render @user.user_products %>
# user_products/_user_product.html.erb
<%= h user_product.product.name %>
<%= h user_product.variety.name if user_product.variety %>
Otherwise something a bit more complicated have to be added. Some of it might be possible to put in the model and controller by using association proxies, but I can't help you there
# users/show
<% for product in @user.products do %>
<%= product.name %>
<%= render :partial => 'variety',
:collection => @user.varieties.find_by_product_id(product.id) %>
<% end %>
# users/_variety
<%= variety.name %>
Splitting it up in partials is of course not necessary (and in this example, maybe a bit ridiculous), but it helps in separating the different parts, especially if you want to add more things to display.
And to answer your questions:
- Since UserProduct is a join model, you don't really need to keep track of it's individual id. You have the User, the Product and the Variety (in those cases it exists). That's all you need, that combination is unique and thus, you can find the record for deletion. Also, a join model should never be edited (provided that you don't have more attributes in it than these fields), It is either create or delete, since all it do is keep the associations.
- Since it is basically a viewing thing it is best to place it in the views. You could of course use the controller or the model to collect all the products and the varieties beforehand by doing something like this:
Moving all of the logic to the controller and the model is probably not a good idea since they are not supposed to know (or care) how you want to display the data. So it is only a matter of preparing it enough.
@products_for_user = []
@user.products.each do |product|
collected_product = {:product => product,
:varieties => @user.varieties.find_by_product_id(product.id)}
@products_for_user << collected_product
end