for purpose of reference: doing many-to-many relationships in Rails

Posted on June 18, 2008. Filed under: HowTo, Ruby on Rails, social web, user accounts management | Tags: , , , , , , , , , , |

There are lots of tutorials out on Rails, and many people may need to work through them to get a grasp of Rails. but once you’re experienced in programming you might feel delayed by all the stuff the tutorial brings up but which you already know. Most of the tutorials I’ve seen so far also prevent you from skipping over to go straightly to the interesting parts because they do stuff in between that affects the interesting parts, so you need to go backwards through the tutorial to figure out what’s affect only and what actually needs to be there so that what you intend will work.

I find that rather annoying. I’d like to have a simple overview of what files need to get modified to get a many-to-many relationship between two tables. For starters, it’s rather useless to know all the details, gimmicks and features you might want to use once you’re a bit more experienced: If you come from a different development language, you probably don’t want anything as simple as a single-table application to get a first impression of Rails for yourself. I want to know how I can implement a many-to-many, and how to make that visible. Nothing more, nothing less.
 

For the example, I use tags and items: Each tag may be related to multiple items; each item may have multiple tags. (For example, the tags {sunset, sun, water} may be related to a sunset picture. Thus, there are multiple tags related to a single picture yet. And another picture might have the tags {tap, water, damp, dishes}. Thus ‘water’ would be a tag that’s related to more but a single item.)

We’ve got three scaffolds to do, one for the items, one for the tags and one for the many-to-many relationship. The three scaffolds create three model files. For simplicity, we start with a whole new project, so there won’t be any side effects of whatever project we might be working on already:

$ rails many_to_many && cd many_to_many
... [output] ...

Next, we do the scaffolds:

$ ./script/generate scaffold item name:string && rake db:migrate
... [output] ...

$ ./script/generate scaffold tag label:string && rake db:migrate
... [output] ...

$ ./script/generate scaffold item_tag item_id:integer tag_id:integer && rake db:migrate
... [output] ...

Once done with that, we have everything in place to set up the many-to-many relationship. We modify the models first, then the output for a single item to make it show the tags for that item also:

file: app/models/item.rb:

class Item < ActiveRecord::Base
  has_many :item_tags
  has_many :tags, :through => :item_tags 
end

(The bold code lines are those you need to insert into the model. Same for the two others below.)
 

file: app/models/tag.rb:

class Tag < ActiveRecord::Base
  has_many :item_tags
  has_many :items, :through => :item_tags 
end

 

file: app/models/item_tag.rb:

class ItemTag < ActiveRecord::Base
  belongs_to :item
  belongs_to :tag 
end

 

And now the output. To meet the needs of the Model View Controller pattern, to get the appropriate output, we need to modify the controller and view of the item(s). First we modify the controller, then the view; in the controller we modify the show() method:

file: app/controllers/items_controller.rb:
show method before:

def show
  @item = Item.find(params[:id])

  respond_to do |format|
    format.html # show.html.erb
    format.xml  { render :x ml => @item }
  end
end

 

show method after:

def show
  @item = Item.find(params[:id])

  @tagline = @item.tags.collect { |t| t.label }.join(', ')
  respond_to do |format|
    format.html # show.html.erb
    format.xml  { render :x ml => @item }
  end
end

 

In the view, we simply add a paragraph that outputs the tagline gotten by the controller:

file: app/views/items/show.html.erb:

<p>
  <b>Tags:</b>
  <%=h @tagline %>
</p>

That mostly is it.

Now you can launch the server by the common

$ ./script/server

and direct your browser to

http://localhost:3000/

You can access items, tags and relationships between both by going to their respective views, like

http://localhost:3000/items
http://localhost:3000/tags
http://localhost:3000/item_tags

There you can enter some dummy data (using the ‘new’ link); to see the effect, I’d start by defining some items, then some tags, then some item_tag relationships, then view the individual item I related some tags to.
 

Notes:

[t]

Make a Comment

Make a Comment: ( 3 so far )

blockquote and a tags work here.

3 Responses to “for purpose of reference: doing many-to-many relationships in Rails”

RSS Feed for Tech/Social/Howto Comments RSS Feed

Galileocomputing released a great openbook: Ruby on Rails 2[1].
I think this should be the first thing to read to get a overview what is possible without the overhead most tutorials call content.

[1] http://www.galileocomputing.de/openbook/ruby_on_rails/

I am new to rails and I found a few tutorials on the subject a bit confusing. This is concise and clear. Very helpful

Eric,

That is a very nice compliment. Thank you!


Where's The Comment Form?

Liked it here?
Why not try sites on the blogroll...