1
votes

I am having a problem with relationships in Rails 4.

There are 4 models

  1. User
  2. Request
  3. Make
  4. Models

A User has_many Requests, a Request has_one Make and a Make has_many Models.

The User->Requests and Make->Models has_many relationships are fine but the Request->Make has_one relationship is failing.

class Request < ActiveRecord::Base
    belongs_to :user
    has_one :make
end

class Make < ActiveRecord::Base
  has_many :models
  belongs_to :request
end

The Schemas for each model are...

create_table "requests", force: true do |t|
  t.integer  "user_id"
  t.integer  "make_id"
  t.datetime "created_at"
  t.datetime "updated_at"
end

create_table "makes", force: true do |t|
  t.string   "name"
  t.datetime "created_at"
  t.datetime "updated_at"
end

When I try to assign a Make to a Request I get the following error

Mysql2::Error: Unknown column 'makes.request_id' in 'where clause': SELECT  `makes`.* FROM `makes`  WHERE `makes`.`request_id` = 7 LIMIT 1                                                                           
ActiveRecord::StatementInvalid Exception: Mysql2::Error: Unknown column 'makes.request_id' in 'where clause': SELECT  `makes`.* FROM `makes`  WHERE `makes`.`request_id` = 7 LIMIT 1                                 
nil  

Why is ActiveRecord requiring a request_id in Make? Wouldn't that only be applicable for a has_many relationship as I have in the User->Requests and Makes->Models relationships?

3

3 Answers

3
votes

The has_one and belongs_to methods allow you to create one-to-one associations between your models, therefore allowing you to easily access each other through various helper methods.

has_one should only be used if the other class contains the "foreign key". If the current class contains the "foreign key", you should use belongs_to instead.

Based on the schema you provided, the associations should be defined like in the following example:

class Request < ActiveRecord::Base
  # Because you have a `make_id` column in the "requests" table.
  belongs_to :make
end

class Make < ActiveRecord::Base
  # Because the Request model has the "foreign key" that 
  # creates the association, in this case `make_id`.
  has_one :request
end

By making the associations like in the example above, Rails will provide you with helper methods that allow you to access or create each associated model:

# Request Model
@request = Request.create!

# Helper methods to access `make`: make, build_make, create_make, make=
@request.make
@request.build_make
@request.create_make
@request.make = Make.create!
# ...


# Make Model
@make = Make.create!

# Helper methods to access `request`
@make.request
@make.build_request
@make.create_request
@make.request = Request.create!
#...

We use the has_one and belongs_to methods to create the associations, but also to have access to all the helper methods that Rails creates for us. Depending on whether you want to access Make from Request or the other way around, you could add or remove the has_one or belongs_to from a particular class.

By keeping each one in both classes though, we get access to a bunch of helper methods from both sides. If for example you removed the belongs_to :make from Request, you wouldn't be able to access make from Request with @request.make. However, you can still access Request from make with @make.request as long as you keep the has_one method.

Remember that migrations are their own thing and we need to setup the associations on the Database level as well by adding the "foreign key" to the correct table. We can easily figure out where to add the foreign key by looking at the definitions of has_one and belongs_to. has_one says that the "foreign key" should be in the associated table, and belongs_to says it should be in this table.

I hope that helps! There is some more information here:

0
votes

Based on your schema and the error message there appears to be no foreign key from requests in makes.

See this, for example:

http://guides.rubyonrails.org/association_basics.html#the-has-one-association

There, Supplier has one Account, and accounts has a supplier_id. Check out the corresponding migration example.

0
votes

You probably just need to add the request_id column to your makes table.

Run:

rails g migration add_request_id_to_makes request_id:integer


bundle exec rake db:migrate