Related table columns

Help for newbie please!

I have two tables: controls and programs.

controls has_many :programs
Programs belongs_to :control

Controls has id, controlname
Programs has, apart from it's own id, two fields ctl1_id and ctl2_id.

These represent two controls that the program relates to.

Foreign keys are set on program table columns ctl1_id and ctl2_id back
to the id field on the control table.

Let's make this more explicit:

class Control < ActiveRecord::Base
   has_many :programs

class Program < ActiveRecord::Base
   belongs_to :ctl1, :class_name => 'Control', :foreign_key => 'ctl1_id'
   belongs_to :ctl2, :class_name => 'Control', :foreign_key => 'ctl2_id'

So when I list the program table using

<% @programs.each do |array| %>
   <td><%= array.ctl1_id%></td>
   <td><%= array.ctl2_id %></td>
<% end %>

...everything is fine.

Any of these:
<% @programs.each do |program| %>
<% @programs.each do |p| %>
<% for program in @programs %>

is probably better than "array" for the block variable. (But this is a personal choice as long as you avoid keywords like "class" or "begin".)

But when I list the programs row I want to actually show the controlname
from controls table rather than the ctl1_id and ctl2_id values from the
programs row. For this I use the following code:-

<% @programs.each do |array| %>
   <td><%= array.control.controlname %></td>
<% end %>

I expect the link via the foreign keys and the models relationships to
give me what I need but I am obviously missing something because I get

You have a nil object when you didn't expect it!
The error occured while evaluating nil.control

Extracted source (around line #56):

53: </tr>
54: <tr>
56: <%= array.control.controlname %></td>

I have checked out the cookbook application code and cant see where I am

Any help gratefully received.


I can see that


<% @programs.each do |program| %>
     <td><%= program.ctl1.controlname %></td>
     <td><%= program.ctl2.controlname %></td>
<% end %>

But personally, I'd rename "controlname" to just "name" since it's a column in the Control model anyway.

From this little example, it seems like you have a program with many (ok, 2) controls, but does each control really associate to more than one program? I'm just wondering whether there's another join model linking programs and controls in a many-to-many relationship.

Anyway, I suspect that you are under the mistaken assumption that the existence of foreign keys in the database have some influence on the behavior of your ActiveRecord models. Other than potentially causing inserts or deletes to fail if performed in the wrong order, your Models won't care.


Rob Biedenharn

this is clearly an example of a many-to-many relationship, an not a
one-to-many relationship as you tried to do with has_many and
A Control can have one or more Programs, and a Program can belong to
one or more controls.

use has_many :through or has_and_belong_to_many
id, controlname

id, name

control_id, program_id

the last table maps the many-to-many relationship between Controls and

Control < ActiveRecord::Base
  has_and_belongs_to_many :programs

Control < ActiveRecord::Base
  has_and_belongs_to_many :controls

@programs = Program.find :all, :include => "controls"

<% @programs.each do |program| %>
     <% program.controls.each do |control| %>
      <%= control.controlname %>
   <% end %>
<% end %>

Well, just changing the code doesn't do the trick ... do you have
associated the programs with the controls?

#programs #controls_programs #controls
id 1 program_id 1 id 2
name TestProg control_id 2 controlname

now controls_programs has an entry that reraltes Program 1 with
Control 2

You achive that like this in your controller:
class Program < ActionController::Base
def add_control
  @program = Program.find(1)
  @control = Control.find(1)
  @program.controls << @control

The your programs and controls are really associated and the view code
should work
You can read more about that here:

also have a look at "has_many :through =>" (google will help) which is
a "better" has_and_belongs_to_many ...
Hope that helps

I already gave you an example in my previous post about how to add a
relationship between a program and a control:
def add_control_to_program
  @program = Program.find(params[:program_id])
  @control = Control.find(params[:control_id])
  @program.controls << @control

Rails then adds the corrept mapping line into the controls_programs
You can also do it vice versa, like:

@control.porgrams << @porgram

the result is the same because of the nature of the many-to-many

for more info read the API Docs about associations please, i already
gave you the links in my previous post.