Has and belongs to many
If you just need to set a simple many-to-many relationship the easiest way to do this is with has_and_belongs_to_many. Here is how your models should look like.
1 2 3 4 5 6 7 | class Post < ActiveRecord::Base has_and_belongs_to_many :tags end class Tag < ActiveRecord::Base has_and_belongs_to_many :posts end |
This association is supported by an additional database table that will store post_id and tag_id for each relationship. This table should be called posts_tags and it will not be created for you, so you will have to create your own migration.

Notice that model names inside join table are in lexical order, so the table will be called posts_tags and not tags_posts (because ‘p’ comes before ‘t’). You can also specify a different name for that table using the :join_table option.
So generate a new migration (you could do this with “rails generate migration create_post_tag_join_table” and enter following code. Then run “rake db:migrate” to make the changes inside your database.
You should use “:id=>false” option in your migration, because join table does not need additional primary key, also notice that this table won’t have “created_at” and “updated_at” timestamps.
1 2 3 4 5 6 7 8 9 10 11 12 | classCreatePostTagJoinTable < ActiveRecord::Migration defself.up create_table :posts_tags, :id => false do |t| t.integer :post_id t.integer :tag_id end end defself.down drop_table:posts_tags end end |
Accessing related posts and tags is now easy. You can get an array of related tags by calling ‘tags’ method.
1 2 | @post = Post.find(1) @tags = @post.tags |
Simplest way to add existing objects is using the << operator. For example:
1 2 | @tag = Tag.find(3) @tag.posts << post |
Or if you want to create brand new object you can use a create method. This method returns created object. The object will be saved in databases, and link through join table will be created.
1 | @tag.posts.create( {:title => 'New title', :content => 'Hello world' }) |
Collections also come with delete method. It will remove objects relationship, but will not destroy the object.
1 | @post.tags.delete( @tag ) |
More info can be found inside rails documentation.
Has many through
In case we need to store additional information inside join table, we will need to use a more complicated version of many-to-many relationship. Lets say that we have two models Student and Test. Each student has taken many tests and each test was taken by many students, but each time a student took a test he got a grade. This is where has_many :through relationship comes in.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 | classStudent < ActiveRecord::Base has_many :grades has_many :tests, :through => :grades end class Grade < ActiveRecord::Base belongs_to :student belongs_to :test end classTest < ActiveRecord::Base has_many :grades has_many :students, :through => :grades end |
Notice that beside setting the has_many :tests relationship, in order to use :through option you need to notify model that it has grades. In other words, you cant ‘go through’ models that you don’t have. Notice that in this case Grade is a normal model, and it must include ID field to work properly. However timestamps may be excluded if you don’t plan to use them.
This relationships behave similar to has_and_belongs_to_many relationship. More info can be found inside rails documentation.








