Home
Ruby on Rails
Relationships
Imagine you have a table of articles for a blog. Now imagine that for each article, you would like to be able to list all similar articles. Or a table containing people, where you would like to link related people to each other. This is a has_and_belongs_to_many association, but there's only one table involved. It's a self-referential relationship: a person has many people, and one of those people may also have many other people, and so on. So how do we implement that in Rails?
Like all other HABTM relationships, we'll need to create a join table that keeps track of which articles are related to one another. However, in this case, both of the ids in the join table will each belong to an article. We'll want a column to keep track of the main article's id, and a column that contains the id of the related article. Let's called this join table related_articles and create a migration...
create_table "related_articles", :force => true, :id => false do |t| t.column "related_article_id", :integer t.column "main_article_id", :integer end
Remember that as this is a join table, it doesn't require its own id, so we set :id => false. Now let's take a look at the Article class...
Article is just like any other class, except we need to be verbose when defining the HABTM relationship. In a standard HABTM relationship, Rails would be able to work out the name of the join table, but in this case, the join table doesn't join two models together, it joins a model back to itself. We need to specify some additional options as follows...
class Article < ActiveRecord::Base has_and_belongs_to_many :related_articles, :class_name => "Article", :join_table => "related_articles", :foreign_key => "main_article_id", :association_foreign_key => "related_article_id" end
So what do each of those options do?
:join_table is where we tell Rails the name of our join table. We called it related_articles. :foreign_key specifies the name of the foreign key column that contains the id of the main article, the one that is acting as a parent of the related articles: we called this main_article_id:association_foreign_key is the name of the foreign key column that contains the id of our related articles--the articles that "belong to" our main article :class_name contains the name of our current class, ArticleA suitable interface to allow us to add related articles would be a select drop-down list that can accept multiple options. Let's edit the form in our new.rhtml view to allow us to select related articles when adding a new article...
<% form_for(@article) do |f| %> <b>Content</b><br /> <%= f.text_field :content %> <b>Title</b><br /> <%= f.text_field :title %> <b>Related articles</b><br /> <%= f.collection_select(:related_article_ids, Article.find(:all), :id, :title, {}, :multiple => true) %> <%= f.submit "Create" %> <% end %>
We use the collection_select helper with the related_article_ids method. This method automatically becomes available when we define a HABTM relationship, and it accepts an array of ids to insert into our join table. We use Article.find(:all) to provide a list of all the other articles in our database, and ask collection_select to format them so that the :id gets sent when we submit the form, but the :title is displayed in the drop-down list. We also enable :multiple => true so that we can choose more than one article.
I won't go into any further details, as drop-down lists for HABTM are a completely separate article, but don't worry--it's one I'll be writing about soon (check the right-hand side to see our very own related articles, as I might already have written it!)
A brilliant plugin that makes changing your database schema even easier and faster.
More
3 MONTHS AGO
Jagannathan Sundaram
About Controllers
Hello, very interesting and very useful lessons. i learn lots of thinks from this codes. Also add lessons for controller and model part.