This article shows how I used Active Record and HABTM to model a many-to-many relationship between entries and categories in my blog.
First I created a migration to build three tables as follows:
create_table "entries", :force => true do |t|
t.column "user_id", :integer
t.column "title", :string
t.column "body", :text
t.column "html_body"
t.column "last_updated_on", :datetime
t.column "created_dt", :datetime
end
create_table "categories", :force => true do |t|
t.column "name", :string
t.column "created_dt", :datetime
end
create_table "categories_entries", :id => false, :force => true do |t|
t.column "category_id", :integer
t.column "entry_id", :integer
end
Note that there are two source tables – entries and categories, and one association table called categories_entries where the association table is made up of two foreign key columns or one primary key from each of the two source tables.
Next I created the models.
class Entry < ActiveRecord::Base
has_and_belongs_to_many :categories
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :entries, :order => "created_dt desc"
end
The main thing to note here is that models are only created for the two source tables and no model is needed for the association table.
And that’s all it took to model my many-to-many relationship using HABTM.
One thing that is very cool about Ruby is the Ruby console. You can use the Ruby console and Active Record to access your domain and to verify that your HABTM is working correctly.
To start the Ruby console I navigated to my application root and typed the following:
c:\rails\tomaso>ruby script/console
This resulted in the following message and prompt:
Loading development environment
>>
To get a list of all entries I typed the following:
>> entrys = Entry.find(:all)
Next to select a single entry from my list of entries I typed the following:
>> entry = entrys[1]
Finally, to verify HABTM I typed the following to get a list of categories for the entry that I had selected.
>> entry.categories
To verify the other side of my HABTM I typed the following to get a list of entries fo a selected category.
>> cats = Category.find(:all)
>> cat = cats[1]
>> cat.entries