#47 Two Many-to-Many
Jun 20, 2007 | 9 minutes | Active Record
There are two different ways to set up a many-to-many association in Rails. In this episode you will see how to implement both ways along with some tips on choosing the right one for your project.
Nice! I was about to call it a day but this popped. Model naming is really tricky.
This came just in time. *Thanks* for not using the P-word. I always wondered if I was doing it wrong when I ran across errors. Will you go deeper... do you call it differently? Category.products still works? Eager loading still works?
Ryan, silly question, but does it matter at all if you use 'category_name' or :category_name? I see that a lot in Rails, some people use symbols, some people use strings, does it make any difference?
Another nice naming convention I've learned is GroupAssignment or CategoryAssignment. Thanks to Chris McGrath for that one.
Andreas, it doesn't really matter. The difference is that if the symbol is already used inside the app, using a symbol version will only leave you with one object in ObjectSpace while using a string will instantiate multiple objects:
irb(main):001:0> :category.object_id
=> 158498
irb(main):002:0> :category.object_id
=> 158498
irb(main):003:0> "category".object_id
=> 265090
irb(main):004:0> "category".object_id
=> 255990
Great railscast Ryan! Can you do one on has-many :through sometime? Thanks!
@Matt and David, I'm planning to do an episode entirely on has_many :through in the near future. In the meantime, @category.products will work and eager loading also works.
@Andreas, normally it doesn't make a difference which one you choose in Rails. However, the naming of associations is an exception. IIRC using strings here will cause problems - stick with symbols as shown in all the examples.
Congrats sir ! Great tutorial. i've been checkin some of them lately and gosh, it's crystal clear. perfect timing, nice timeline, handy topics, everything's just great. Keep on the good work and bravo again for all that knowledge you share with others.
Hey great job Ryan this is super helpful. You helped me out on RailsForum and mention self-referencing many-to-many association. Is there a way I could get more help on that somehow (this doesn't seem like the appropriate place)? Perhaps email? or a good link?
@Jack, it's probably best to keep all detailed questions on the forums (which you are already doing, I'm just replying for the benefit of others).
I just found your podcast from iTunes tonight and watched this episode, great stuff! Thank you!
Hey thanks for all the great screencasts, they are so so cool.
There's just one itch I have.
I've actually never used the has_and_belongs_to_many relationship for an app because when I started railing the REST and the has_many through technique was all the buzz.
However in one of your previous screencasts you had a very interesting method for a habtm relationships
collection_singular_ids=
which allowed you to update all the associated records in the join table by passing in a collection of new ids, it seemed that all the deletion of records that weren't in this new list were automatically done for you, if this is what is happening its very useful and I was wondering if the has_many through technique has a similar method?
Hi Ryan, I normally add a composite primary key to the habtm join table like this:
add_index :categorizations, [:product_id, :category_id], :primary => true
... and foreign key associations. Is there any reason not to? Or did you omit it to keep it simple?
@Joshua, the "category_ids=" method is one of the few reasons you may want to stick with has_and_belongs_to_many since has_many :throug doesn't have an equivilant. Of course you can still make your own if you're using has_many :through.
That said, many times the join table has extra information in it if you're using has_many :through. In that case you wouldn't want the interface to be simple checkboxes as shown in the earlier episode. You would need some way to manage the extra data in the join.
@Zubin, thanks for the suggestion. I didn't have any reason for omiting the composite primary key and probably should have included it. I usually only add keys and indexes when they become needed, but I think a HABTM join table is a good case where it's always needed.
@Ryan: I think a Railscast on :has_many :through with polymorphic associations would be really nice. I find lots of documentation on :has_many :through, and on polymorphism. But, hardly anything with them together. Also, if you do this, could you please make sure to go through the controller workflow (ie. what gets created and when). Again, this is an area that is almost never mentioned. The focus is almost always on the model. Also, could the has_many_polymorphs plugin be the answer?
so simple and clear, thanks!
by the way, Ryan, how do you toggle words so quickly in textmate?
for example when you was printing drop_table 'categories_products' in the migration.
there was no hotkey displayed :).
The escape key is what I used. It will look at the rest of the document and do some basic auto-completion.
A railscast that goes into more detail on has_many :through and polymorphic associations would be greatly appreciated.
after change to use has_many :through,it doesn't auto add the "product_ids" and "product_ids=" methods.
Then how to update the association ?
@RainChen, right, this is one reason you may want to stick with habtm. Some methods like "product_ids=" are not available so you'll have to create them manually if you need them.
Normally, to update the association you either create or update the join model itself. Such as "categoirzation.new" or "categorization.update_attributes". You can also use the "<<" method to create the join. See this for details:
http://blog.hasmanythrough.com/2006/8/19/magic-join-model-creation
<< is for adding.
How about removing the association?
When calling @product.categorizations.delete, nothing happened.
if calling @production.categorizations.clear
it gave a sql error some thing like " DELETE FROM categories_products WHERE `id` = NULL".(Because I use the legacy habtm table without autoincrement id )
so how to deal with this case?
Every model needs an auto-incrementing id column in Rails, so I recommend remaking a "categorizations" table and not trying to use the legacy categories_products table.
So it seems not so easy to change to use has_many :through.
How about the has_many_polymorphs plugin?
It is said that it could saves lives ^0^
I was wondering if anybody could tell me the best way to avoid duplicates with a has many through relationship. I basically want a pair of ids to be unique in the database...
thanks in advance
@jeff, you can avoid duplicates by using the validates_uniqueness_of method. You'll likely need to use the :scope option to include the other columns.
You said that eager loading works. What's the correct protocol for eager loading with a has_many :through relationship?
@Ryan Long, eager loading should work with has_many :through as well without any differences. It just does a mutli-table join.
Nice movie. Very helpful.
Do you intend t oupdate this for the current version? Would it be something like:
def self.up
create_table 'categories_products', :id => false do |t|
t.belongs_to :category
t.belongs_to :product
end
end
What the heck is kino trying to say?
thanks a lot!
@ryan:
Any chance you can do a screencast repeating #17 with the has_many :through and incorporating some additional field storage?
Hi guys,
When I have the 2 models : product and category done and the table categories_products ready, how do I proceed with it?
if I have p = @product.first
and p.categories.size gives me the correct count BUT how do I make new relation between the existing category and product.
p.categories.new attempts to create the category?
Thanks
Hey Ryan,
kompliment, your tips are really great.Good job. But I have for this example a question
Products => Lord Of The Ring
Category => Movies
Sub_categories => DVD
Genre => Fantasy
Can I also use many to many for this example. Or there is something better.
how can we handle adding new product(and its category)procedure using categorization model?
before that , i've been using category_id column in products table and i can add using form_for(@product).
Now how can i relate product and its category in the same form page?
How do you find all Products that do not have any categories?
I have a query that works but I'd like to have that in a named_scope.
Any ideas?
img_shells_with_out_users = ImageShell.find_by_sql 'SELECT * FROM image_shells INNER JOIN image_shells_users ON (image_shells_users.image_shell_id!=image_shells.id)'
Excellent tutorial. I am smiling for the first time today. Because it makes sense now, and it works! Thank you!
Thank you very much! Helped me a lot.
Good tutorial
How do you add tuples to this
relationship? And how do you access one product's categories?
Hi, I have followed your cast but i keep getting an error when testing it in the console.
I have a user model, hotel model and relationship model. The relationship model being the look-up table. When i use the example in the the screen cast with my models then run User.all to show all users in the console i get the following error?
http://pastie.org/1059009
Models
http://pastie.org/1059011
ideas anyone?
Ryan, this episode is really useful but it would be great if you can show it in action. For example how do you go about assigning multiple categories to a product and viceversa from the FRONT END. In one of your comments above you mentioned "@Matt and David, I'm planning to do an episode entirely on has_many :through in the near future" But I did not see an episode on has_many :through. Please be kind and do one.
Crystal clear, thanks
has_many :taxonomic_links, :foreign_key => 'name_id'
belongs_to :taxon_name
@taxon_names = TaxonName.find(:all, :conditions=>{:genus_name => params[:gm], :corrig => params[:ck1], :type_species => params[:ck2], :sp_epithet => params[:sp]}, :joines => :taxonomic_links)
% taxon_name.taxonomic_links.each do |tax_link| %>
<%= tax_link.text %><% end %>
Its not connecting... where is the problem??
Thanks for this article, it certainly helped me in deciding the best way to implement many-to-many relationships
Along the lines of what @Fred said 4 years ago, I've found plenty of explanations on how to setup the models for many-to-many associations, but I have no idea how to setup the corresponding view and controller.
Something like http://railscasts.com/episodes/196-nested-model-form-part-1 but for many-to-many relations would be very helpful.
Thank you. This one is still very helpful four years later.
Thanks a lot. At last, I got it! :)
Hey Ryan! Thanks a lot!
I have a little problem here.
Do i have to make any changes in my controller? The view is perfect, but when I click "send", nothing is updated. Thanks!
I got the same problem!
Check this episode, will help.
http://railscasts.com/episodes/17-habtm-checkboxes-revised?view=asciicast
This is a bit of an older post, and I am new to rails, but I had a problem with this railscast. I had to add this:
accepts_nested_attributes_for :categorizations
to the product model in order to allow me to create a new products with categories. Is this something that is new since this railscast was created?
A starting question would be, where do I find good read for the kind of question I have here?
I'm using the 'through' approach but I'm stumbling over some basics.
The models are set up correctly, but in my show.html.erb
I want to check for the existence of the other object before I show a value from it.
I'm running into 'undefined method' when I
<% if product.category %> <-- error
<% product.category.each do |pc| ...
Do I need to add methods to my categorization model, or some assignments in my controller for one of the models?
I followed along but am getting this error
Can't mass-assign protected attributes: skill_ids
For whatever reason it doesn't understand the _ids (with the extra 's'). I tried to add :skill_ids in the attr_accessible area but it just gives me another issues:
unknown attribute: skill_ids
HELP!
Can you post your code? Are you using HABTM or HMT? Have you created a join table? You will definitely need
attr_accessible :skill_ids
for Rails 2/3, orparams.require(:model).permit(:skill_ids)
in the controller if you're using Rails 4. I'm happy to help if you can share your code. Maybe add it to a gist and then reference it in a reply.I think you need to add
attr_accessor :skill_ids to it.
@Dave Johnson, in case you were must have been using "has many through" and were getting this error.
Your join table should not contain the column "skill_ids",it should have skill_id.
That would solve the problem.
Reply is late but will help others :)
Let's say I had a relationship of Athlete's and Match's where each Match was a contest between two Athletes. Is it appropriate in this case to use HABTM or has_many through: or is it better to just store the two athlete id's in the Match model? Thanks!
This episode has been updated for Rails 5 as a blog post. Has Many Through and Has and Belongs to Many in Rails 5