77

I'm struggling now to get HATBM working correctly. I have a beaten scanario: articles and tags. I presume, HABTM should be used here, since it is a many-to-many relationship. I don't know however if I should manually create a join table (articles_tags in this case).

My code currently as follows:

class Article < ActiveRecord::Base
  has_and_belongs_to_many :tags  
end

class Tag < ActiveRecord::Base
  has_and_belongs_to_many :articles
end

When I run the migrations, no 3rd table is created. Also, I would like to add that my third table doesn't bear any domain logic, just blind assignment.

I'm using Rails 2.2.2

Raj
  • 22,346
  • 14
  • 99
  • 142
Valentin V
  • 24,971
  • 33
  • 103
  • 152

4 Answers4

140

You should do this in a migration of one of the tables, or in a separate migration if those migrations have been ran:

create_table :articles_tags, :id => false do |t|
  t.references :article, :tag
end

add_index :articles_tags, [:article_id, :tag_id]

This will create the table for you and the :id => false tells Rails not to add an id field to this table. There's an index also, which will speed up lookups for this join table.

You could also generate a model (ArticlesTag) for this and do:

# article.rb
has_many :articles_tags
has_many :tags, :through => :articles_tags

# tag.rb
has_many :articles_tags
has_many :articles, :through => :articles_tags

# article_tag.rb
belongs_to :tag
belongs_to :article

And then create the table in the migration generated from the script/generate model articles_tag call.

Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
  • 12
    Funny that official guides or documentation does not state this. I had to dig on Stackoverflow. Ty. – lzap Mar 07 '11 at 08:14
  • What if you wanted to relate them on with a unique name for the join because that join was already being used. – Trip Jul 26 '11 at 16:01
  • I think you should add indexes to join table by adding `add_index :articles_tags, [:article_id, :tag_id]` before the end of `change` method – Giovanni Cappellotto Feb 15 '12 at 16:49
  • Should it not be :articles_tag (singular) in the model? – Benjineer Apr 01 '14 at 11:48
  • I've added create_table :articles_tags... to my Tag migration. What I should do so my rake db:rollback works correctly? – Skodik.o Nov 30 '15 at 19:07
8

Note that this is covered in the API.

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_and_belongs_to_many

Sai Perchard
  • 853
  • 1
  • 10
  • 12
7

You probably also want to add an index to the migration:

add_index "articles_tags", "article_id"

add_index "articles_tags", "tag_id"

However, if you want tagging functionality I'd recommend the acts_as_taggable_on rails plugin:

http://www.intridea.com/tag/acts_as_taggable_on http://github.com/mbleigh/acts-as-taggable-on/

I've used it on a project and it was very easy to implement.

One of the issues with a join table for tagging is that it can easily get ugly creating a join table for each content type you wish to make taggable (ie. comments_tags, posts_tags, images_tags, etc). This plugin uses a taggings table which includes a discriminator to determine the content type without the need of a specific join table for each type.

Jack Chu
  • 6,791
  • 4
  • 38
  • 44
  • Migration methods references (or belongs_to) automatically adds indexes. You do not need that. You can break your migration with it. – lzap Mar 07 '11 at 08:44
  • Migration methods references (or belongs_to) really do not automatically adds indexes. (Thought they do :-) – lzap Mar 07 '11 at 08:52
  • IMHO it would be better to create a composite primary key. But it's database specific. – Art Shayderov Mar 19 '11 at 17:10
1

In combination with this Qeuestion(1st answear) How to set up a typical users HABTM roles relationship and 1st answear from here, it has to be understood even by a monkey. I am new in RoR and it's got working like a charm

Community
  • 1
  • 1
mArtinko5MB
  • 756
  • 8
  • 18