0

I have two models: User and Location as below:

class User < ActiveRecord::Base
  attr_accessible :location, :password, :user_name, :password_confirmation

  validates :location, :user_name, :presence => true
  validates :password, :presence => true, :confirmation => true

  has_one :location, :foreign_key => 'location'

end

class Location < ActiveRecord::Base
  attr_accessible :loc_id, :loc_name

  belongs_to :user, :foreign_key => 'loc_id'
end

You can see that I use the custom foreign_key for the models. I use form builder to build a user sign up form, but when I submit data the error occurs:

Location(#2170327880) expected, got String

I use simple_form to build the form, related code is:

= f.input :location, :collection => Location.all.collect {|c| [c.loc_name, c.loc_id]}

How can I resolve this problem? Or must I use the default foreign_key like location_id for the association?

Thanks.

Update:

When I rename the location field in User model to loc_id and remove the :foreign_key like this:

class User < ActiveRecord::Base
  attr_accessible :loc_id, :password, :user_name, :password_confirmation

  validates :loc_id, :user_name, :presence => true
  validates :password, :presence => true, :confirmation => true

  has_one :location, :foreign_key => 'location'

end

class Location < ActiveRecord::Base
  attr_accessible :loc_id, :loc_name

  belongs_to :user
end

It works fine. But I still want to know how to associate the User and Location model.

P.S. I use Location model to store the country code and country name, which will never update by User.

zhu0qun
  • 3
  • 1
  • 4
  • Can more than one user have the same location ? – Frederick Cheung Jun 08 '12 at 15:59
  • Yes. The `location` table is a static data table. I will save all the city in the table and give it a unique ID. In `user` table I will preserve the city id instead of the city name. – zhu0qun Jun 09 '12 at 08:09

4 Answers4

1

It looks like you're misusing foreign key. In the User model, you should have just has_one :location and the location model should have a user_id attribute. In the location model, you only need to write belongs_to :user. A foreign key is always an index into another (foreign) table.

cdesrosiers
  • 8,862
  • 2
  • 28
  • 33
  • Thanks for your answer. I am a newbie to Rails, could you explain how can I build association between the `location` and `loc_id` field? – zhu0qun Jun 08 '12 at 08:45
1

It sounds like you actually want to have

class User < ActiveRecord::Base
  belongs_to :location
end

class Location < ActiveRecord::Base
  has_many :users
end

This means that a user has a location_id column. If you do things the other way around (user_id column on location) then a given location can only be associated to one user. The rails way is that location_id on users 'points' at the id column in the locations table. If you want it to point at a different column, use the :primary_key option (The :foreign_key option would be if you wanted the column on users to be called something other than location_id)

In terms of the form, you can't do f.select :location - forms don't know how to transfer a complicated object like that. In these cases you want to set the form to control the location_id attribute, i.e.

= f.input :location_id, :collection => Location.all.collect {|c| [c.loc_name, c.id]}

If you go down the route of having the location id column refer to the loc_id column on location, then you'd need to change that to be

= f.input :location_id, :collection => Location.all.collect {|c| [c.loc_name, c.loc_id]}

Personally if you're only just starting out with rails I'd stick to the defaults

Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
0
class User < ActiveRecord::Base
  # stuff

  has_one :location    
end

class Location < ActiveRecord::Base
  # more stuff

  belongs_to :user
end
Kevin D.
  • 1,500
  • 1
  • 18
  • 41
  • Hi, I add `user_id` field to the Location table, but the error still occur :( – zhu0qun Jun 08 '12 at 08:51
  • how exactly did you add the user_id column? Through migrations? Can you post your schema.rb file here? – Kevin D. Jun 08 '12 at 09:04
  • Yes, I use migrations to add column to location table. I just updated my question, I find the error disappear when I rename the column `location` to `loc_id`, but I don't know how can I build association between these models directly. – zhu0qun Jun 08 '12 at 09:07
  • scratch that, DONT ADD the user_id column to the Location table, my brain wasn't working awhile ago. The foreign key should reside in the User table. And what do you mean "build associations between these models directly"? – Kevin D. Jun 08 '12 at 09:32
  • I mean in the sql sentence I can do like `select * from user, location where user.location = location.loc_id`. How can I connect user.location and location.loc_id in Rails models? Can I use `:foreign_key` to achieve that? – zhu0qun Jun 08 '12 at 15:19
0

If what you ultimately want to do is select all users with the same location, you might want to set the models up a little differently. Right now, because each user has its own location record in the location table, you would end up with duplicate locations, and you'd have to find all of these for a unique location and extract the user_ids from them.

Instead do this

class User < ActiveRecord::Base
   has_one :placement
   has_one :location, through: :placement
end

class Placement < ActiveRecord::Base
    belongs_to :user
    belongs_to :location
end

class Location < ActiveRecord::Base
    has_many :placements
    has_many :users, through: :placements
end

. As for the migrations, Placement should have a :user_id and :location_id. You can drop the :user_id that you currently have in Location. What this code says is that we have many users and we have many unique locations, and we place users in unique locations by creating placements, which indicate that a user with :user_id is located in location with :location_id. Also, don't forget to add a the line

add_index :placements, [:user_id, :location_id], unique: true

so that you can't place a user in a location more than once.

EDIT: Forgot to add: you can get all users in a location by simply getting the location record and calling location.users

cdesrosiers
  • 8,862
  • 2
  • 28
  • 33