Modelling Foreign Keys


#1

Can someone point me to a reference or tutorial that shows how to map
foreign key relationships in the model?

For example given:

Users
id
name
email

Posts
id
user_id
title

How do I associate user_id with users.id in the Post and User models?
has_many and belongs_to don’t seem to do it.


#2

Arch S. wrote:

Can someone point me to a reference or tutorial that shows how to map
foreign key relationships in the model?

For example given:

Users
id
name
email

Posts
id
user_id
title

How do I associate user_id with users.id in the Post and User models?
has_many and belongs_to don’t seem to do it.

This cheatsheet might be helpful:

http://slash7.com/cheats/activerecord_cheatsheet.pdf

Regards,
Andre


#3

In this case, why won’t it?

In your user model, you’d say

has_many :posts

and in the post model you’d say

belongs_to :user

and that’s all you need for what you describe. What’s not working?

Arch S. wrote:

Can someone point me to a reference or tutorial that shows how to map
foreign key relationships in the model?

For example given:

Users
id
name
email

Posts
id
user_id
title

How do I associate user_id with users.id in the Post and User models?
has_many and belongs_to don’t seem to do it.


#4

So that cheat sheet raised some questions. First it sounds like it is
saying not to use has_many and belongs_to with foreign keys??

I guess what I want an example of is how to set my simple example up so
that in the basica scaffold view I can edit/select the values for a
property that is a foreign key. So from the post view I want to
select/assign the user.


#5

Steve K. wrote:

In this case, why won’t it?

In your user model, you’d say

has_many :posts

and in the post model you’d say

belongs_to :user

and that’s all you need for what you describe. What’s not working?

Well in the view I get no visibility to the foreign keys. Just now I
found this example:

class Student < ActiveRecord::Base
set_table_name “students”
set_primary_key “id”

belongs_to :advisor, :class_name=>“Advisor”, :foreign_key =>
“advisor_id”
belongs_to :second_advisor, :class_name=>“Advisor”, :foreign_key =>
“sub_advisor”

has_and_belongs_to_many :courses, :class_name=>“Course”

end

I want my posts/edit to allow me to select a user.


#6

Chris H. wrote:

models:

class User < ActiveRecord::Base
has_many :posts
end

class Post < ActiveRecord::Base
beongs_to :user #
end

Thanks Chris.

So, what do I get from explicitly identifying the foreign key
relationship in the model (like belongs_to :advisor,
:class_name=>“Advisor”, :foreign_key =>
“advisor_id”)??

Thanks.


#7

models:

class User < ActiveRecord::Base
has_many :posts
end

class Post < ActiveRecord::Base
beongs_to :user #
end

controller

class PostController < ApplicationController
def new
@post = Post.new
@users = User.find(:all)
end
end

view (new.rhtml)

<%= start_form_tag :action => “create” %>

<%= select :post, :user_id, options_from_collection_for_select(@users,
“id”,
“name”) %>

<%= end_form_tag %>


#8

you’re not getting anything. Rails figures out that info based on the
criteria you specify. if you stick to Rails conventions, you don’t have
to
do anything but provide the association name, rails does the rest.

so in that example

student belongs_to :advisor

rails will make the following assumptions:

students table contains an “advisor_id” column that references the id
column
in advisors table
the associated class is named “Advisor”

example 2:

student belongs_to :advisor, :class_name => “Person”

in this case, we must tell the model to not make the default
assumptions,
since our association name has nothing to do with what we really want.
you
have to be explicit if you don’t want to stick to Rails conventions


#9

Chris H. wrote:

you’re not getting anything. Rails figures out that info based on the
criteria you specify. if you stick to Rails conventions, you don’t have
to
do anything but provide the association name, rails does the rest.

so in that example

student belongs_to :advisor

rails will make the following assumptions:

students table contains an “advisor_id” column that references the id
column
in advisors table
the associated class is named “Advisor”

I guess I do have a follow up question.

In the above example, what is gained by adding a has_many relationship
to Advisor?

advisor has_many :students


#10

Chris H. wrote:

you’re not getting anything. Rails figures out that info based on the
criteria you specify. if you stick to Rails conventions, you don’t have
to
do anything but provide the association name, rails does the rest.

Thank you Chris that was extremely helpful. It makes sense now.


#11

I’m really having a hard time implementing foreign keys and I find it
odd that Rails makes so many things easy, yet foreign keys don’t seem to
be supported. Maybe I am missing something. Here’s the revelant set up
info:

Table Test
id
title
summary
test_type_id

Table Test_Type
id
type_name

class Admin::TestController < Admin::BaseController
scaffold :test
def new
@test = Test.new
@test_type = TestType.find(:all)
end
end

class Admin::TestTypeController < Admin::BaseController
scaffold :test_type

end

class Test < ActiveRecord::Base
has_many :questions
has_many :logged_tests
has_many :results
belongs_to :test_type
belongs_to :user
end

class TestType < ActiveRecord::Base
has_many :tests
end

views/admin/test/new.rhtml

<%= start_form_tag :action => “create” %>

Title
<%= text_field_tag("test_title", nil, :size => "40") %>

Summary
<%= text_area_tag("test_summary", nil, :size => "40x20") %>

<%= select :test, :test_type, options_from_collection_for_select(@test_types, "id", "type_name") %>

<%= submit_tag(value = "Create New Test") %>

<%= end_form_tag() %>

Back

--------------------- error:

ActionView::TemplateError (undefined method `inject’ for nil:NilClass)
on line #19 of app/views/admin/test/new.rhtml:
16: <%= text_area_tag(“test_summary”, nil, :size => “40x20”) %>
17:


18:


19: <%= select :test, :test_type,
options_from_collection_for_select(@test_types, “id”, “type_name”) %>
20:


21:


22: <%= submit_tag(value = “Create New Test”) %>

/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_view/helpers/form_options_helper.rb:141:in

options_from_collection_for_select' #{RAILS_ROOT}/app/views/admin/test/new.rhtml:19 /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_view/base.rb:268:insend’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_view/base.rb:268:in
compile_and_render_template' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_view/base.rb:244:inrender_template’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_view/base.rb:205:in
render_file' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/base.rb:655:inrender_file’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/base.rb:595:in
render_with_no_layout' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/layout.rb:228:inrender_with_a_layout’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/base.rb:684:in
render_with_layout' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/base.rb:645:inrender_action’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/scaffolding.rb:153:in
render_scaffold' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/scaffolding.rb:117:innew’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/base.rb:853:in
send' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/base.rb:853:inperform_action_without_filters’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/filters.rb:332:in
perform_action_without_benchmark' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/benchmarking.rb:69:inperform_action_without_rescue’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/benchmarking.rb:69:in
measure' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/benchmarking.rb:69:inperform_action_without_rescue’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/rescue.rb:82:in
perform_action' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/base.rb:369:insend’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/base.rb:369:in
process_without_session_management_support' /usr/lib/ruby/gems/1.8/gems/actionpack-1.11.2/lib/action_controller/session_management.rb:116:inprocess’
/usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/dispatcher.rb:38:in
dispatch' /usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/fcgi_handler.rb:141:inprocess_request’
/usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/fcgi_handler.rb:53:in
process!' /usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/fcgi_handler.rb:52:ineach_cgi’
/usr/lib/ruby/1.8/fcgi.rb:597:in each' /usr/lib/ruby/1.8/fcgi.rb:597:ineach_cgi’
/usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/fcgi_handler.rb:52:in
process!' /usr/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/fcgi_handler.rb:22:inprocess!’
dispatch.fcgi:33

Any help appreciated.


#12

d6veteran wrote:

I’m really having a hard time implementing foreign keys and I find it
odd that Rails makes so many things easy, yet foreign keys don’t seem to
be supported. Maybe I am missing something. Here’s the revelant set up
info:

You could use the scaffolding to build a working example of the logic
here.

Your problem on line 19, is that you have a variable @test_types, which
you didn’t define in your controller. The
options_from_collection_for_select calls inject on this nil object and
you get this error.

Ray


#13

Ray B. wrote:

d6veteran wrote:

I’m really having a hard time implementing foreign keys and I find it
odd that Rails makes so many things easy, yet foreign keys don’t seem to
be supported. Maybe I am missing something. Here’s the revelant set up
info:

You could use the scaffolding to build a working example of the logic
here.

Your problem on line 19, is that you have a variable @test_types, which
you didn’t define in your controller. The
options_from_collection_for_select calls inject on this nil object and
you get this error.

Ray

I thought I was using the scaffolding.

So you mean in the TestTypeController I need to define @test_types?


#14

Arch S. wrote:

I thought I was using the scaffolding.

I was wrong about this. I thought that the scaffolding created the
correct scaffolding for the associations, but it doesn’t.

So you mean in the TestTypeController I need to define @test_types?

Yes. There won’t be anything in the drop down until you create some
test_types though.

Ray


#15

Ray B. wrote:

Arch S. wrote:

So you mean in the TestTypeController I need to define @test_types?

Yes. There won’t be anything in the drop down until you create some
test_types though.

Ray

That doesn’t make any sense to me. I look through the rest of my
controllers and I can access the entire model without anything more than
a scaffold declaration.

I understand that I should define test_type in my Type controller, as I
have done here:

class Admin::TestController < Admin::BaseController
scaffold :test
def new
@test = Test.new
@test_type = TestType.find(:all)
end
end

class Admin::TestTypeController < Admin::BaseController
scaffold :test_type

end

Not sure why this is not working.


#16

Arch S. wrote:

Not sure why this is not working.

You are referencing @test_types in you view, not @test_type.

You are defining @test_type in your controller, not @test_types.

They need to be the same.


#17

The point of telling Rails about the relationships between models is not
because Rails will magically create scaffolding for you but because
you’ll be able to access you data through the relations.

For instance, once you’ve set up those two relationships in your models,
you can do things like

@advisor = Advisor.find(1)
@students = @advisor.students

and conversely, if you have a student selected into @student, you can
get the advisor’s name simply via @student.advisor.name

Or if you’re updating a student’s attributes in a controller action, if
you set some of @student.advisor’s attributes, the changes to the
advisor will also get saved when you simply call @student.save

and all sorts of stuff like that.

Arch S. wrote:

Chris H. wrote:
… snip …

so in that example

student belongs_to :advisor

rails will make the following assumptions:

students table contains an “advisor_id” column that references the id
column
in advisors table
the associated class is named “Advisor”

I guess I do have a follow up question.

In the above example, what is gained by adding a has_many relationship
to Advisor?

advisor has_many :students


#18

Steve K. wrote:

The point of telling Rails about the relationships between models is not
because Rails will magically create scaffolding for you but because
you’ll be able to access you data through the relations.

For instance, once you’ve set up those two relationships in your models,
you can do things like

@advisor = Advisor.find(1)
@students = @advisor.students

and conversely, if you have a student selected into @student, you can
get the advisor’s name simply via @student.advisor.name

Or if you’re updating a student’s attributes in a controller action, if
you set some of @student.advisor’s attributes, the changes to the
advisor will also get saved when you simply call @student.save

and all sorts of stuff like that.

Thanks for the reply. I cleaned up my typos and tried calling
@test.test_type.id and it works. That is cool.


#19

associations are a two way street.

in the student advisors example

student belongs_to :advisor

s = Student.find(1, :include => :advisor) # find student with id = 1
puts s.advisor.name # print name of the student’s advisor

advisor has_many :students

a = Advisor.find(1, :include => :students) # find advisor with id = 1
a.students.each { |s| puts s.name } # print all the names of the
students
for this advisor