Using associations (belongs_to, has_many) in Ruby on Rails.

April 4th, 2008  | Tags: , , , ,

Probably you already needed or you will need in the future to create associations between the models in Ruby on Rails.

As you know, Rails uses the ActiveRecord as ORM. The ORM is responsible for the associations. Fortunately in Rails it is a simple task.

In this topic I am going to show you how to create the associations many-to-one (the most common).

Understanding the association

Create relationship between models in a common task in any application. Let’s take a look at the example below:


A State can have many cities, e.g: NY state has: New York City, Rochester, Buffalo, Albany and so on.
A City belongs to only a single State, e.g: San Diego belongs to CA.

This is a relationship known as many-to-one. Note the state_id field in the Cities table. Each city belongs to only a single State, so you must save the ID of the State into the Cities table. it does make sense, doesn’t it?

Creating a simple application to see how the association does work

Using the model above, let’s create a simple application that show up all the Cities and their States. Also, let’s create a functionality to add new cities (not new states).

First of all, let’s create the rails project. Simply type the following:

rails RailsAssociation --database=mysql

Change the config/database.yml file according to your database configuration.

Now let’s create both model (State and City).

State

ruby script/generate model state name:string

City

ruby script/generate model city name:string state_id:integer

We are going to use only one field for each model. Note: in the City model we’ve got a state_id field.
Remember: Although the model names are state and city, the table name will be states and cities.

Now let’s run the rake and create the tables physically into the database.

rake db:migrate

Let’s add some values into the database directly. (We are not cover in this topic how to create the NEW functionality to states).

INSERT INTO states (name) values ('NY');
INSERT INTO states (name) values ('CA');
INSERT INTO cities (name, state_id) values ('New York City',1);
INSERT INTO cities (name, state_id) values ('Buffalo',1);
INSERT INTO cities (name, state_id) values ('San Jose',2);
INSERT INTO cities (name, state_id) values ('Rochester',1);
INSERT INTO cities (name, state_id) values ('San Francisco',2);

What has been done? Well, we added two states (NY and CA) and also we added 5 citie, 2 belong to CA and 3 belong to NY.

Now it is time to create the controller and the views. Actually let’s create two views: new and list

ruby script/generate controller cities new list

Inside the cities controller, put the following code in the list method

def list
@cities = City.find(:all)
end

It simply fetch all cities from the database and put them in a @cities variable.

On the view, put the following in the file app/views/cities/list.html.erb

<h3>List of the cities</h3>
<% for city in @cities %>
<%= h(city.name) %> - <%= h(city.state.name) %><br />
<% end %>

Let’s pay attention on the lines above. We are iteration over the @cities variable and showing its content on the screen. Check it out the h(city.name) and h(city.state.name). The first is going to show the city’s name and the second the city state’s name. But wait a moment!! How does rails know the city.state.name? Where the state attribute was declared?

We must change the model to make the associations. So far rails doesn’t know nothing about the associations, but with few lines it will does.

Open the app/model/state.rb file and put the following:

has_many :cities

You just informed to Rails with a State has many cities. It’s simple, huh?

Now open the city.rb and add

belongs_to :state

Hey look at the code above!!! the :state attribute was declared there!! Now rails knows about the city.state.name. Rails is magic, isn’t it?

Now run the server ruby script/server and enter in the following url: http://localhost:3000/cities/list, you should see a screen like this:

We’ve got the list functionality done, now let’s create the add. Open the file app/views/cities/add.html.erb and add the following content:

<h3>New City</h3>
<% if flash[:notice] %>
<p><%= h(flash[:notice]) %></p>
<% end %>
<% form_for :city do |form| %>
Name: <%= form.text_field :name %><br/>
state: <%= form.collection_select :state_id ,@states,:id,:name %><br/>
<%= submit_tag "Save" %>
<% end %>
<%= link_to "List",:action => :list %>

As as normal form, we have two fiels (a text_field and a drop-down).

Now open the controller cities and implement the method new with the following content:

def new
if request.post?
@city = City.new(params[:city])
if @city.save!
flash[:notice] = "City has been saved successfully"
else
flash[:notice] = "Error saving the city. Contact support"
logger.error("Error saving the city")
end
end
@states = State.find(:all)
end

Nothing different, huh?

Try to run the application again and enter in the URL: http://localhost:3000/cities/new

You can download this simple application here. Remember to change the config/database.yml before run the server.

  1. Faustino
    July 17th, 2008 at 19:40
    #1

    Thanks…

  2. GoGo
    July 30th, 2008 at 03:10
    #2

    Great tutorial, thanks! How you do this if you were doing metro areas. There are some places where a metro area is right on the border of two states thereby allowing it to belong to more than one state. So most cases “metro area” belongs_to “state” and “state” has many “metro areas”. But there are a few “metro areas” that belong to more than one state. THanks.

  3. jrjuniorsp
    July 30th, 2008 at 10:37
    #3

    Hei GoGo,

    Thanks for your reply.
    In your case (metro areas belongs to more than one states) you must use the association MANY to MANY, not Many to One like the article.
    Unfortunately I didn’t write a topic about many to many in Ruby on Rails yet, but certainly you can find a good tutorial on the internet.

    Regards,
    Jair

  4. Everton
    March 26th, 2010 at 08:45
    #4

    Ola.
    Sera que poderia me ajudar?
    Preciso criar uma associacao similar ao que vc citou no exemplo.
    Porem a diferenca eh que no meu caso, a associacao eh feita com usuario e dependentes.
    O usuario e os dependentes deste usuario deveriam ser criados simultaneamente. Ou seja, vou preencher os dados do usuario Jose e tambem os dependentes Mariazinha e Joana.
    Att.

  5. April 3rd, 2010 at 20:14
    #5

    Hi All Im New, Iv been browsing around this forum for a few hours as a guest. I found it very and it has helped out allot. I hope to stay around for some time and give something back.

    Many Thanks.

TOP